[
  {
    "path": ".gitattributes",
    "content": "*.golden -text\n*.svg binary\n**/testdata/** -text\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "patreon: dominikh\ngithub: dominikh\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1_false_positive.md",
    "content": "---\nname: 💢 False positive in Staticcheck\nabout: Your code is fine but Staticcheck complains about it, anyway.\nlabels: false-positive, needs-triage\ntitle: \"\"\n---\n<!--\nPlease make sure to include the following information in your issue report:\n\n- The output of 'staticcheck -version'\n- The output of 'staticcheck -debug.version' (it is fine if this command fails)\n- The output of 'go version'\n- The output of 'go env'\n- Exactly which command you ran\n- Output of the command and what's wrong with the output\n- Where we can read the code you're running Staticcheck on\n  (GitHub repo, link to playground, code embedded in the issue, ...)\n-->\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/2_false_negative.md",
    "content": "---\nname: 🦆 False negative in Staticcheck\nabout: Your code is wrong but Staticcheck doesn't complain about it.\nlabels: false-negative, needs-triage\ntitle: \"\"\n---\n<!--\nPlease make sure to include the following information in your issue report:\n\n- The output of 'staticcheck -version'\n- The output of 'staticcheck -debug.version' (it is fine if this command fails)\n- The output of 'go version'\n- The output of 'go env'\n- Exactly which command you ran\n- Output of the command and what's wrong with the output\n- Where we can read the code you're running Staticcheck on\n  (GitHub repo, link to playground, code embedded in the issue, ...)\n-->\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/3_bug.md",
    "content": "---\nname: 🐞 General bugs with Staticcheck\nabout: Something in Staticcheck isn't working as it should.\nlabels: bug, needs-triage\ntitle: \"\"\n---\n<!--\nPlease make sure to include the following information in your issue report:\n\n- The output of 'staticcheck -version'\n- The output of 'staticcheck -debug.version' (it is fine if this command fails)\n- The output of 'go version'\n- The output of 'go env'\n- Exactly which command you ran\n- Output of the command and what's wrong with the output\n- Where we can read the code you're running Staticcheck on\n  (GitHub repo, link to playground, code embedded in the issue, ...)\n-->\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/4_other.md",
    "content": "---\nname: 🛠 Other\nabout: Ideas, feature requests, and all other issues not fitting into another category.\nlabels: needs-triage\ntitle: \"\"\n---\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: \"CI\"\non: [\"push\", \"pull_request\"]\n\njobs:\n  ci:\n    name: \"Run CI\"\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [\"windows-latest\", \"ubuntu-latest\", \"macOS-latest\"]\n        go: [\"1.25\", \"1.26\"]\n        godebug: [\"gotypesalias=0\", \"gotypesalias=1\"]\n    runs-on: ${{ matrix.os }}\n    steps:\n    - uses: actions/checkout@v1\n      with:\n        fetch-depth: 1\n    - uses: actions/setup-go@v6\n      with:\n        go-version: ${{ matrix.go }}\n    - run: \"go test ./...\"\n      env:\n        GODEBUG: ${{ matrix.godebug }}\n    - run: \"go vet ./...\"\n    - uses: dominikh/staticcheck-action@v1\n      with:\n        version: \"2026.1\"\n        min-go-version: \"module\"\n        install-go: false\n        cache-key: ${{ matrix.go }}\n        output-format: binary\n        output-file: \"./staticcheck.bin\"\n    - uses: actions/upload-artifact@v4\n      with:\n        name: \"staticcheck-${{ github.sha }}-${{ matrix.go }}-${{ matrix.os }}-${{ matrix.godebug }}.bin\"\n        path: \"./staticcheck.bin\"\n        retention-days: 1\n        if-no-files-found: warn\n  output:\n    name: \"Output Staticcheck findings\"\n    needs: ci\n    runs-on: \"ubuntu-latest\"\n    steps:\n    - uses: actions/setup-go@v6\n      with:\n        go-version: \"stable\"\n    # this downloads all artifacts of the current workflow into the current working directory, creating one directory per artifact\n    - uses: actions/download-artifact@v4\n    - id: glob\n      run: |\n        # We replace newlines with %0A, which GitHub apparently magically turns back into newlines\n        out=$(ls -1 ./staticcheck-*.bin/*.bin)\n        echo \"::set-output name=files::${out//$'\\n'/%0A}\"\n    - uses: dominikh/staticcheck-action@v1\n      with:\n        install-go: false\n        merge-files: ${{ steps.glob.outputs.files }}\n"
  },
  {
    "path": ".gitignore",
    "content": "/cmd/keyify/keyify\n/cmd/staticcheck/staticcheck\n/cmd/structlayout-optimize/structlayout-optimize\n/cmd/structlayout-pretty/structlayout-pretty\n/cmd/structlayout/structlayout\n/dist/20??.?.?/\n/dist/20??.?/\n/internal/cmd/irdump/irdump\n/website/.hugo_build.lock\n/website/public\n/website/resources\n/website/assets/img/sponsors\n/website/data/sponsors.toml\n/website/data/copyrights.toml\n/website/data/checks.json\n/website/content/docs/configuration/default_config/index.md\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"website/themes/docsy\"]\n\tpath = website/themes/docsy\n\turl = https://github.com/google/docsy\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2016 Dominik Honnef\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "LICENSE-THIRD-PARTY",
    "content": "Staticcheck and its related tools make use of third party projects,\neither by reusing their code, or by statically linking them into\nresulting binaries. These projects are:\n\n* The Go Programming Language - https://golang.org/\n  golang.org/x/mod - https://github.com/golang/mod\n  golang.org/x/tools - https://github.com/golang/tools\n  golang.org/x/sys - https://github.com/golang/sys\n  golang.org/x/xerrors - https://github.com/golang/xerrors\n\n    Copyright (c) 2009 The Go Authors. All rights reserved.\n\n    Redistribution and use in source and binary forms, with or without\n    modification, are permitted provided that the following conditions are\n    met:\n\n       * Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n       * Redistributions in binary form must reproduce the above\n    copyright notice, this list of conditions and the following disclaimer\n    in the documentation and/or other materials provided with the\n    distribution.\n       * Neither the name of Google Inc. nor the names of its\n    contributors may be used to endorse or promote products derived from\n    this software without specific prior written permission.\n\n    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n    \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\n* github.com/BurntSushi/toml - https://github.com/BurntSushi/toml\n\n    The MIT License (MIT)\n\n    Copyright (c) 2013 TOML authors\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in\n    all copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n    THE SOFTWARE.\n\n* gogrep - https://github.com/mvdan/gogrep\n\n    Copyright (c) 2017, Daniel Martí. All rights reserved.\n\n    Redistribution and use in source and binary forms, with or without\n    modification, are permitted provided that the following conditions are\n    met:\n\n       * Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n       * Redistributions in binary form must reproduce the above\n    copyright notice, this list of conditions and the following disclaimer\n    in the documentation and/or other materials provided with the\n    distribution.\n       * Neither the name of the copyright holder nor the names of its\n    contributors may be used to endorse or promote products derived from\n    this software without specific prior written permission.\n\n    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n    \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n* gosmith - https://github.com/dvyukov/gosmith\n\n    Copyright (c) 2014 Dmitry Vyukov. All rights reserved.\n\n    Redistribution and use in source and binary forms, with or without\n    modification, are permitted provided that the following conditions are\n    met:\n\n       * Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n       * Redistributions in binary form must reproduce the above\n    copyright notice, this list of conditions and the following disclaimer\n    in the documentation and/or other materials provided with the\n    distribution.\n       * The name of Dmitry Vyukov may be used to endorse or promote\n    products derived from this software without specific prior written permission.\n\n    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n    \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n\t<h1><img alt=\"Staticcheck logo\" src=\"/images/logo.svg\" height=\"300\" /><br />\n\t\tThe advanced Go linter\n\t</h1>\n</div>\n\nStaticcheck is a state of the art linter for the [Go programming\nlanguage](https://go.dev/). Using static analysis, it finds bugs and performance issues,\noffers simplifications, and enforces style rules.\n\n**Financial support by [private and corporate sponsors](https://staticcheck.dev/sponsors) guarantees the tool's continued development.\nPlease [become a sponsor](https://github.com/users/dominikh/sponsorship) if you or your company rely on Staticcheck.**\n\n\n## Documentation\n\nYou can find extensive documentation on Staticcheck on [its website](https://staticcheck.dev/docs/).\n\n## Installation\n\n### Releases\n\nIt is recommended that you run released versions of the tools.\nThese releases can be found as git tags (e.g. `2022.1`).\n\nThe easiest way of installing a release is by using `go install`, for example `go install honnef.co/go/tools/cmd/staticcheck@2022.1`.\nAlternatively, we also offer [prebuilt binaries](https://github.com/dominikh/go-tools/releases).\n\nYou can find more information about installation and releases in the [documentation](https://staticcheck.dev/docs/getting-started/).\n\n### Master\n\nYou can also run the master branch instead of a release. Note that\nwhile the master branch is usually stable, it may still contain new\nchecks or backwards incompatible changes that break your build. By\nusing the master branch you agree to become a beta tester.\n\n## Tools\n\nAll of the following tools can be found in the cmd/ directory. Each\ntool is accompanied by its own README, describing it in more detail.\n\n| Tool                                               | Description                                                             |\n|----------------------------------------------------|-------------------------------------------------------------------------|\n| [staticcheck](cmd/staticcheck/)                    | Go static analysis, detecting bugs, performance issues, and much more. |\n| [structlayout](cmd/structlayout/)                  | Displays the layout (field sizes and padding) of structs.               |\n| [structlayout-optimize](cmd/structlayout-optimize) | Reorders struct fields to minimize the amount of padding.               |\n| [structlayout-pretty](cmd/structlayout-pretty)     | Formats the output of structlayout with ASCII art.                      |\n\n## Libraries\n\nIn addition to the aforementioned tools, this repository contains the\nlibraries necessary to implement these tools.\n\nUnless otherwise noted, none of these libraries have stable APIs.\nTheir main purpose is to aid the implementation of the tools.\nYou'll have to expect semiregular backwards-incompatible changes if you decide to use these libraries.\n\n## System requirements\n\nStaticcheck can be compiled and run with the latest release of Go. It can analyze code targeting any version of Go upto\nthe latest release.\n"
  },
  {
    "path": "_benchmarks/bench.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\ndeclare -A PKGS=(\n\t[\"strconv\"]=\"strconv\"\n\t[\"net/http\"]=\"net/http\"\n\t[\"image/color\"]=\"image/color\"\n\t[\"std\"]=\"std\"\n\t[\"k8s\"]=\"k8s.io/kubernetes/pkg/...\"\n)\n\nMIN_CORES=32\nMAX_CORES=32\nINCR_CORES=2\nMIN_GOGC=100\nMAX_GOGC=100\nSAMPLES=10\nWIPE_CACHE=1\nFORMAT=bench\nBIN=$(realpath ./silent-staticcheck.sh)\n\nrunBenchmark() {\n\tlocal pkg=\"$1\"\n\tlocal label=\"$2\"\n\tlocal gc=\"$3\"\n\tlocal cores=\"$4\"\n\tlocal wipe=\"$5\"\n\n\tif [ $wipe -ne 0 ]; then\n\t\trm -rf ~/.cache/staticcheck\n\tfi\n\n\tlocal out=$(GOGC=$gc GOMAXPROCS=$cores env time -f \"%e %M\" $BIN $pkg 2>&1)\n\tlocal t=$(echo \"$out\" | cut -f1 -d\" \")\n\tlocal m=$(echo \"$out\" | cut -f2 -d\" \")\n\tlocal ns=$(printf \"%s 1000000000 * p\" $t | dc)\n\tlocal b=$((m * 1024))\n\n\tcase $FORMAT in\n\t\tbench)\n\t\t\tprintf \"BenchmarkStaticcheck-%s-GOGC%d-wiped%d-%d  1   %.0f ns/op  %.0f B/op\\n\" \"$label\" \"$gc\" \"$wipe\" \"$cores\" \"$ns\" \"$b\"\n\t\t\t;;\n\t\tcsv)\n\t\t\tprintf \"%s,%d,%d,%d,%.0f,%.0f\\n\" \"$label\" \"$gc\" \"$cores\" \"$wipe\" \"$ns\" \"$b\"\n\t\t\t;;\n\tesac\n}\n\nexport GO111MODULE=off\n\nif [ \"$FORMAT\" = \"csv\" ]; then\n\tprintf \"packages,gogc,gomaxprocs,wipe-cache,time,memory\\n\"\nfi\n\nfor label in \"${!PKGS[@]}\"; do\n\tpkg=${PKGS[$label]}\n\tfor gc in $(seq $MIN_GOGC 10 $MAX_GOGC); do\n\t\tfor cores in $(seq $MIN_CORES $INCR_CORES $MAX_CORES); do\n\t\t\tfor i in $(seq 1 $SAMPLES); do\n\t\t\t\trunBenchmark \"$pkg\" \"$label\" \"$gc\" \"$cores\" 1\n\t\t\t\trunBenchmark \"$pkg\" \"$label\" \"$gc\" \"$cores\" 0\n\t\t\tdone\n\t\tdone\n\tdone\ndone\n"
  },
  {
    "path": "_benchmarks/silent-staticcheck.sh",
    "content": "#!/usr/bin/env sh\n/home/dominikh/prj/src/honnef.co/go/tools/cmd/staticcheck/staticcheck -checks \"all\" -fail \"\" $1 &>/dev/null\nexit 0\n"
  },
  {
    "path": "add-check.go",
    "content": "//go:build ignore\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"go/format\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\t\"text/template\"\n)\n\nvar tmpl = `\npackage {{.lname}}\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName: \"{{.name}}\",\n\t\tRun: run,\n\t\tRequires: []*analysis.Analyzer{},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"\",\n\t\tText: {{.emptyRaw}},\n\t\t{{- if .quickfix }}\n\t\tBefore: {{.emptyRaw}},\n\t\tAfter: {{.emptyRaw}},\n\t\t{{- end }}\n\t\tSince: \"Unreleased\",\n\t\tSeverity: lint.SeverityWarning,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\treturn nil, nil\n}\n`\n\nfunc main() {\n\tlog.SetFlags(0)\n\n\tvar t template.Template\n\tif _, err := t.Parse(tmpl); err != nil {\n\t\tlog.Fatalln(\"couldn't parse template:\", err)\n\t}\n\n\tif len(os.Args) != 2 {\n\t\tlog.Fatalf(\"Usage: %s <new check's name>\", os.Args[0])\n\t}\n\n\tname := os.Args[1]\n\tcheckRe := regexp.MustCompile(`^([A-Za-z]+)\\d{4}$`)\n\tparts := checkRe.FindStringSubmatch(name)\n\tif parts == nil {\n\t\tlog.Fatalf(\"invalid check name %q\", name)\n\t}\n\n\tvar catDir string\n\tprefix := strings.ToUpper(parts[1])\n\tswitch prefix {\n\tcase \"SA\":\n\t\tcatDir = \"staticcheck\"\n\tcase \"S\":\n\t\tcatDir = \"simple\"\n\tcase \"ST\":\n\t\tcatDir = \"stylecheck\"\n\tcase \"QF\":\n\t\tcatDir = \"quickfix\"\n\tdefault:\n\t\tlog.Fatalf(\"unknown check prefix %q\", prefix)\n\t}\n\n\tlname := strings.ToLower(name)\n\tdir := filepath.Join(catDir, lname)\n\tdst := filepath.Join(dir, lname+\".go\")\n\n\tmkdirp(dir)\n\n\tbuf := bytes.NewBuffer(nil)\n\tvars := map[string]any{\n\t\t\"name\":     name,\n\t\t\"lname\":    lname,\n\t\t\"emptyRaw\": \"``\",\n\t\t\"quickfix\": prefix == \"QF\",\n\t}\n\n\tif err := t.Execute(buf, vars); err != nil {\n\t\tlog.Fatalf(\"couldn't generate %s: %s\", dst, err)\n\t}\n\n\tb, err := format.Source(buf.Bytes())\n\tif err != nil {\n\t\tlog.Fatalf(\"couldn't gofmt %s: %s\", dst, err)\n\t}\n\n\twriteFile(dst, b)\n\n\ttestdata := filepath.Join(dir, \"testdata\", \"src\", \"example.com\", \"pkg\")\n\tmkdirp(testdata)\n\twriteFile(filepath.Join(testdata, \"pkg.go\"), []byte(\"package pkg\\n\"))\n\n\tout, err := exec.Command(\"go\", \"generate\", \"./...\").CombinedOutput()\n\tif err != nil {\n\t\tlog.Printf(\"could not run 'go generate ./...': %s\", err)\n\t\tlog.Println(\"Output:\")\n\t\tlog.Fatalln(string(out))\n\t}\n\n\tflags := []string{\n\t\t\"add\",\n\t\t\"--intent-to-add\",\n\t\t\"--verbose\",\n\n\t\tfilepath.Join(dir, lname+\"_test.go\"),\n\t\tfilepath.Join(testdata, \"pkg.go\"),\n\t\tdst,\n\t}\n\tcmd := exec.Command(\"git\", flags...)\n\tcmd.Stdout = os.Stdout\n\tcmd.Stderr = os.Stderr\n\tif err := cmd.Run(); err != nil {\n\t\tlog.Fatalln(\"could not run 'git add':\", err)\n\t}\n}\n\nfunc writeFile(path string, data []byte) {\n\tif err := os.WriteFile(path, data, 0677); err != nil {\n\t\tlog.Fatalf(\"couldn't write %s: %s\", path, err)\n\t}\n}\n\nfunc mkdirp(path string) {\n\tif err := os.MkdirAll(path, 0777); err != nil {\n\t\tlog.Fatalf(\"couldn't create directory %s: %s\", path, err)\n\t}\n}\n"
  },
  {
    "path": "analysis/callcheck/callcheck.go",
    "content": "// Package callcheck provides a framework for validating arguments in function calls.\npackage callcheck\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/types\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n)\n\ntype Call struct {\n\tPass  *analysis.Pass\n\tInstr ir.CallInstruction\n\tArgs  []*Argument\n\n\tParent *ir.Function\n\n\tinvalids []string\n}\n\nfunc (c *Call) Invalid(msg string) {\n\tc.invalids = append(c.invalids, msg)\n}\n\ntype Argument struct {\n\tValue    Value\n\tinvalids []string\n}\n\ntype Value struct {\n\tValue ir.Value\n}\n\nfunc (arg *Argument) Invalid(msg string) {\n\targ.invalids = append(arg.invalids, msg)\n}\n\ntype Check func(call *Call)\n\nfunc Analyzer(rules map[string]Check) func(pass *analysis.Pass) (any, error) {\n\treturn func(pass *analysis.Pass) (any, error) {\n\t\treturn checkCalls(pass, rules)\n\t}\n}\n\nfunc checkCalls(pass *analysis.Pass, rules map[string]Check) (any, error) {\n\tcb := func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) {\n\t\tobj, ok := callee.Object().(*types.Func)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tr, ok := rules[typeutil.FuncName(obj)]\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tvar args []*Argument\n\t\tirargs := site.Common().Args\n\t\tif callee.Signature.Recv() != nil {\n\t\t\tirargs = irargs[1:]\n\t\t}\n\t\tfor _, arg := range irargs {\n\t\t\tif iarg, ok := arg.(*ir.MakeInterface); ok {\n\t\t\t\targ = iarg.X\n\t\t\t}\n\t\t\targs = append(args, &Argument{Value: Value{arg}})\n\t\t}\n\t\tcall := &Call{\n\t\t\tPass:   pass,\n\t\t\tInstr:  site,\n\t\t\tArgs:   args,\n\t\t\tParent: site.Parent(),\n\t\t}\n\t\tr(call)\n\n\t\tvar astcall *ast.CallExpr\n\t\tswitch source := site.Source().(type) {\n\t\tcase *ast.CallExpr:\n\t\t\tastcall = source\n\t\tcase *ast.DeferStmt:\n\t\t\tastcall = source.Call\n\t\tcase *ast.GoStmt:\n\t\t\tastcall = source.Call\n\t\tcase nil:\n\t\t\t// TODO(dh): I am not sure this can actually happen. If it\n\t\t\t// can't, we should remove this case, and also stop\n\t\t\t// checking for astcall == nil in the code that follows.\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"unhandled case %T\", source))\n\t\t}\n\n\t\tfor idx, arg := range call.Args {\n\t\t\tfor _, e := range arg.invalids {\n\t\t\t\tif astcall != nil {\n\t\t\t\t\tif idx < len(astcall.Args) {\n\t\t\t\t\t\treport.Report(pass, astcall.Args[idx], e)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// this is an instance of fn1(fn2()) where fn2\n\t\t\t\t\t\t// returns multiple values. Report the error\n\t\t\t\t\t\t// at the next-best position that we have, the\n\t\t\t\t\t\t// first argument. An example of a check that\n\t\t\t\t\t\t// triggers this is checkEncodingBinaryRules.\n\t\t\t\t\t\treport.Report(pass, astcall.Args[0], e)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treport.Report(pass, site, e)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor _, e := range call.invalids {\n\t\t\treport.Report(pass, call.Instr, e)\n\t\t}\n\t}\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\teachCall(fn, cb)\n\t}\n\treturn nil, nil\n}\n\nfunc eachCall(fn *ir.Function, cb func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function)) {\n\tfor _, b := range fn.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tif site, ok := instr.(ir.CallInstruction); ok {\n\t\t\t\tif g := site.Common().StaticCallee(); g != nil {\n\t\t\t\t\tcb(fn, site, g)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc ExtractConstExpectKind(v Value, kind constant.Kind) *ir.Const {\n\tk := extractConst(v.Value)\n\tif k == nil || k.Value == nil || k.Value.Kind() != kind {\n\t\treturn nil\n\t}\n\treturn k\n}\n\nfunc ExtractConst(v Value) *ir.Const {\n\treturn extractConst(v.Value)\n}\n\nfunc extractConst(v ir.Value) *ir.Const {\n\tv = irutil.Flatten(v)\n\tswitch v := v.(type) {\n\tcase *ir.Const:\n\t\treturn v\n\tcase *ir.MakeInterface:\n\t\treturn extractConst(v.X)\n\tdefault:\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "analysis/code/code.go",
    "content": "// Package code answers structural and type questions about Go code.\npackage code\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/build/constraint\"\n\t\"go/constant\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"go/version\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/facts/purity\"\n\t\"honnef.co/go/tools/analysis/facts/tokenfile\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/knowledge\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\ntype Positioner interface {\n\tPos() token.Pos\n}\n\nfunc IsOfStringConvertibleByteSlice(pass *analysis.Pass, expr ast.Expr) bool {\n\ttyp, ok := pass.TypesInfo.TypeOf(expr).Underlying().(*types.Slice)\n\tif !ok {\n\t\treturn false\n\t}\n\telem := types.Unalias(typ.Elem())\n\tif version.Compare(LanguageVersion(pass, expr), \"go1.18\") >= 0 {\n\t\t// Before Go 1.18, one could not directly convert from []T (where 'type T byte')\n\t\t// to string. See also https://github.com/golang/go/issues/23536.\n\t\telem = elem.Underlying()\n\t}\n\treturn types.Identical(elem, types.Typ[types.Byte])\n}\n\nfunc IsOfPointerToTypeWithName(pass *analysis.Pass, expr ast.Expr, name string) bool {\n\tptr, ok := types.Unalias(pass.TypesInfo.TypeOf(expr)).(*types.Pointer)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn typeutil.IsTypeWithName(ptr.Elem(), name)\n}\n\nfunc IsOfTypeWithName(pass *analysis.Pass, expr ast.Expr, name string) bool {\n\treturn typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(expr), name)\n}\n\nfunc IsInTest(pass *analysis.Pass, node Positioner) bool {\n\t// FIXME(dh): this doesn't work for global variables with\n\t// initializers\n\tf := pass.Fset.File(node.Pos())\n\treturn f != nil && strings.HasSuffix(f.Name(), \"_test.go\")\n}\n\n// IsMain reports whether the package being processed is a package\n// main.\nfunc IsMain(pass *analysis.Pass) bool {\n\treturn pass.Pkg.Name() == \"main\"\n}\n\n// IsMainLike reports whether the package being processed is a\n// main-like package. A main-like package is a package that is\n// package main, or that is intended to be used by a tool framework\n// such as cobra to implement a command.\n//\n// Note that this function errs on the side of false positives; it may\n// return true for packages that aren't main-like. IsMainLike is\n// intended for analyses that wish to suppress diagnostics for\n// main-like packages to avoid false positives.\nfunc IsMainLike(pass *analysis.Pass) bool {\n\tif pass.Pkg.Name() == \"main\" {\n\t\treturn true\n\t}\n\tfor _, imp := range pass.Pkg.Imports() {\n\t\tif imp.Path() == \"github.com/spf13/cobra\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc SelectorName(pass *analysis.Pass, expr *ast.SelectorExpr) string {\n\tinfo := pass.TypesInfo\n\tsel := info.Selections[expr]\n\tif sel == nil {\n\t\tswitch x := expr.X.(type) {\n\t\tcase *ast.Ident:\n\t\t\tpkg, ok := info.ObjectOf(x).(*types.PkgName)\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Sprintf(\"(%s).%s\", info.TypeOf(x), expr.Sel.Name)\n\t\t\t}\n\t\t\treturn fmt.Sprintf(\"%s.%s\", pkg.Imported().Path(), expr.Sel.Name)\n\t\tcase *ast.SelectorExpr:\n\t\t\treturn fmt.Sprintf(\"(%s).%s\", SelectorName(pass, x), expr.Sel.Name)\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"unsupported selector: %v\", expr))\n\t\t}\n\t}\n\tif v, ok := sel.Obj().(*types.Var); ok && v.IsField() {\n\t\treturn fmt.Sprintf(\"(%s).%s\", typeutil.DereferenceR(sel.Recv()), sel.Obj().Name())\n\t} else {\n\t\treturn fmt.Sprintf(\"(%s).%s\", sel.Recv(), sel.Obj().Name())\n\t}\n}\n\nfunc IsNil(pass *analysis.Pass, expr ast.Expr) bool {\n\treturn pass.TypesInfo.Types[expr].IsNil()\n}\n\nfunc BoolConst(pass *analysis.Pass, expr ast.Expr) bool {\n\tval := pass.TypesInfo.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val()\n\treturn constant.BoolVal(val)\n}\n\nfunc IsBoolConst(pass *analysis.Pass, expr ast.Expr) bool {\n\t// We explicitly don't support typed bools because more often than\n\t// not, custom bool types are used as binary enums and the explicit\n\t// comparison is desired. We err on the side of false negatives and\n\t// treat aliases like other custom types.\n\n\tident, ok := expr.(*ast.Ident)\n\tif !ok {\n\t\treturn false\n\t}\n\tobj := pass.TypesInfo.ObjectOf(ident)\n\tc, ok := obj.(*types.Const)\n\tif !ok {\n\t\treturn false\n\t}\n\tbasic, ok := c.Type().(*types.Basic)\n\tif !ok {\n\t\treturn false\n\t}\n\tif basic.Kind() != types.UntypedBool && basic.Kind() != types.Bool {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc ExprToInt(pass *analysis.Pass, expr ast.Expr) (int64, bool) {\n\ttv := pass.TypesInfo.Types[expr]\n\tif tv.Value == nil {\n\t\treturn 0, false\n\t}\n\tif tv.Value.Kind() != constant.Int {\n\t\treturn 0, false\n\t}\n\treturn constant.Int64Val(tv.Value)\n}\n\nfunc ExprToString(pass *analysis.Pass, expr ast.Expr) (string, bool) {\n\tval := pass.TypesInfo.Types[expr].Value\n\tif val == nil {\n\t\treturn \"\", false\n\t}\n\tif val.Kind() != constant.String {\n\t\treturn \"\", false\n\t}\n\treturn constant.StringVal(val), true\n}\n\nfunc CallName(pass *analysis.Pass, call *ast.CallExpr) string {\n\t// See the comment in typeutil.FuncName for why this doesn't require special handling\n\t// of aliases.\n\n\tfun := astutil.Unparen(call.Fun)\n\n\t// Instantiating a function cannot return another generic function, so doing this once is enough\n\tswitch idx := fun.(type) {\n\tcase *ast.IndexExpr:\n\t\tfun = idx.X\n\tcase *ast.IndexListExpr:\n\t\tfun = idx.X\n\t}\n\n\t// (foo)[T] is not a valid instantiation, so no need to unparen again.\n\n\tswitch fun := fun.(type) {\n\tcase *ast.SelectorExpr:\n\t\tfn, ok := pass.TypesInfo.ObjectOf(fun.Sel).(*types.Func)\n\t\tif !ok {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn typeutil.FuncName(fn)\n\tcase *ast.Ident:\n\t\tobj := pass.TypesInfo.ObjectOf(fun)\n\t\tswitch obj := obj.(type) {\n\t\tcase *types.Func:\n\t\t\treturn typeutil.FuncName(obj)\n\t\tcase *types.Builtin:\n\t\t\treturn obj.Name()\n\t\tdefault:\n\t\t\treturn \"\"\n\t\t}\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\nfunc IsCallTo(pass *analysis.Pass, node ast.Node, name string) bool {\n\t// See the comment in typeutil.FuncName for why this doesn't require special handling\n\t// of aliases.\n\n\tcall, ok := node.(*ast.CallExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn CallName(pass, call) == name\n}\n\nfunc IsCallToAny(pass *analysis.Pass, node ast.Node, names ...string) bool {\n\t// See the comment in typeutil.FuncName for why this doesn't require special handling\n\t// of aliases.\n\n\tcall, ok := node.(*ast.CallExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\tq := CallName(pass, call)\n\treturn slices.Contains(names, q)\n}\n\nfunc File(pass *analysis.Pass, node Positioner) *ast.File {\n\tm := pass.ResultOf[tokenfile.Analyzer].(map[*token.File]*ast.File)\n\treturn m[pass.Fset.File(node.Pos())]\n}\n\n// BuildConstraints returns the build constraints for file f. It considers both //go:build lines as well as\n// GOOS and GOARCH in file names.\nfunc BuildConstraints(pass *analysis.Pass, f *ast.File) (constraint.Expr, bool) {\n\tvar expr constraint.Expr\n\tfor _, cmt := range f.Comments {\n\t\tif len(cmt.List) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, el := range cmt.List {\n\t\t\tif el.Pos() > f.Package {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif line := el.Text; strings.HasPrefix(line, \"//go:build\") {\n\t\t\t\tvar err error\n\t\t\t\texpr, err = constraint.Parse(line)\n\t\t\t\tif err != nil {\n\t\t\t\t\texpr = nil\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tname := pass.Fset.PositionFor(f.Pos(), false).Filename\n\toexpr := constraintsFromName(name)\n\tif oexpr != nil {\n\t\tif expr == nil {\n\t\t\texpr = oexpr\n\t\t} else {\n\t\t\texpr = &constraint.AndExpr{X: expr, Y: oexpr}\n\t\t}\n\t}\n\n\treturn expr, expr != nil\n}\n\nfunc constraintsFromName(name string) constraint.Expr {\n\tname = filepath.Base(name)\n\tname = strings.TrimSuffix(name, \".go\")\n\tname = strings.TrimSuffix(name, \"_test\")\n\tvar goos, goarch string\n\tswitch strings.Count(name, \"_\") {\n\tcase 0:\n\t\t// No GOOS or GOARCH in the file name.\n\tcase 1:\n\t\t_, c, _ := strings.Cut(name, \"_\")\n\t\tif _, ok := knowledge.KnownGOOS[c]; ok {\n\t\t\tgoos = c\n\t\t} else if _, ok := knowledge.KnownGOARCH[c]; ok {\n\t\t\tgoarch = c\n\t\t}\n\tdefault:\n\t\tn := strings.LastIndex(name, \"_\")\n\t\tif _, ok := knowledge.KnownGOOS[name[n+1:]]; ok {\n\t\t\t// The file name is *_stuff_GOOS.go\n\t\t\tgoos = name[n+1:]\n\t\t} else if _, ok := knowledge.KnownGOARCH[name[n+1:]]; ok {\n\t\t\t// The file name is *_GOOS_GOARCH.go or *_stuff_GOARCH.go\n\t\t\tgoarch = name[n+1:]\n\t\t\t_, c, _ := strings.Cut(name[:n], \"_\")\n\t\t\tif _, ok := knowledge.KnownGOOS[c]; ok {\n\t\t\t\t// The file name is *_GOOS_GOARCH.go\n\t\t\t\tgoos = c\n\t\t\t}\n\t\t} else {\n\t\t\t// The file name could also be something like foo_windows_nonsense.go — and because nonsense\n\t\t\t// isn't a known GOARCH, \"windows\" won't be interpreted as a GOOS, either.\n\t\t}\n\t}\n\n\tvar expr constraint.Expr\n\tif goos != \"\" {\n\t\texpr = &constraint.TagExpr{Tag: goos}\n\t}\n\tif goarch != \"\" {\n\t\tif expr == nil {\n\t\t\texpr = &constraint.TagExpr{Tag: goarch}\n\t\t} else {\n\t\t\texpr = &constraint.AndExpr{X: expr, Y: &constraint.TagExpr{Tag: goarch}}\n\t\t}\n\t}\n\treturn expr\n}\n\n// IsGenerated reports whether pos is in a generated file. It ignores\n// //line directives.\nfunc IsGenerated(pass *analysis.Pass, pos token.Pos) bool {\n\t_, ok := Generator(pass, pos)\n\treturn ok\n}\n\n// Generator returns the generator that generated the file containing\n// pos. It ignores //line directives.\nfunc Generator(pass *analysis.Pass, pos token.Pos) (generated.Generator, bool) {\n\tfile := pass.Fset.PositionFor(pos, false).Filename\n\tm := pass.ResultOf[generated.Analyzer].(map[string]generated.Generator)\n\tg, ok := m[file]\n\treturn g, ok\n}\n\n// MayHaveSideEffects reports whether expr may have side effects. If\n// the purity argument is nil, this function implements a purely\n// syntactic check, meaning that any function call may have side\n// effects, regardless of the called function's body. Otherwise,\n// purity will be consulted to determine the purity of function calls.\nfunc MayHaveSideEffects(pass *analysis.Pass, expr ast.Expr, purity purity.Result) bool {\n\tswitch expr := expr.(type) {\n\tcase *ast.BadExpr:\n\t\treturn true\n\tcase *ast.Ellipsis:\n\t\treturn MayHaveSideEffects(pass, expr.Elt, purity)\n\tcase *ast.FuncLit:\n\t\t// the literal itself cannot have side effects, only calling it\n\t\t// might, which is handled by CallExpr.\n\t\treturn false\n\tcase *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:\n\t\t// types cannot have side effects\n\t\treturn false\n\tcase *ast.BasicLit:\n\t\treturn false\n\tcase *ast.BinaryExpr:\n\t\treturn MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Y, purity)\n\tcase *ast.CallExpr:\n\t\tif purity == nil {\n\t\t\treturn true\n\t\t}\n\t\tswitch obj := typeutil.Callee(pass.TypesInfo, expr).(type) {\n\t\tcase *types.Func:\n\t\t\tif _, ok := purity[obj]; !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\tcase *types.Builtin:\n\t\t\tswitch obj.Name() {\n\t\t\tcase \"len\", \"cap\":\n\t\t\tdefault:\n\t\t\t\treturn true\n\t\t\t}\n\t\tdefault:\n\t\t\treturn true\n\t\t}\n\t\tfor _, arg := range expr.Args {\n\t\t\tif MayHaveSideEffects(pass, arg, purity) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\tcase *ast.CompositeLit:\n\t\tif MayHaveSideEffects(pass, expr.Type, purity) {\n\t\t\treturn true\n\t\t}\n\t\tfor _, elt := range expr.Elts {\n\t\t\tif MayHaveSideEffects(pass, elt, purity) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\tcase *ast.Ident:\n\t\treturn false\n\tcase *ast.IndexExpr:\n\t\treturn MayHaveSideEffects(pass, expr.X, purity) || MayHaveSideEffects(pass, expr.Index, purity)\n\tcase *ast.IndexListExpr:\n\t\t// In theory, none of the checks are necessary, as IndexListExpr only involves types. But there is no harm in\n\t\t// being safe.\n\t\tif MayHaveSideEffects(pass, expr.X, purity) {\n\t\t\treturn true\n\t\t}\n\t\tfor _, idx := range expr.Indices {\n\t\t\tif MayHaveSideEffects(pass, idx, purity) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\tcase *ast.KeyValueExpr:\n\t\treturn MayHaveSideEffects(pass, expr.Key, purity) || MayHaveSideEffects(pass, expr.Value, purity)\n\tcase *ast.SelectorExpr:\n\t\treturn MayHaveSideEffects(pass, expr.X, purity)\n\tcase *ast.SliceExpr:\n\t\treturn MayHaveSideEffects(pass, expr.X, purity) ||\n\t\t\tMayHaveSideEffects(pass, expr.Low, purity) ||\n\t\t\tMayHaveSideEffects(pass, expr.High, purity) ||\n\t\t\tMayHaveSideEffects(pass, expr.Max, purity)\n\tcase *ast.StarExpr:\n\t\treturn MayHaveSideEffects(pass, expr.X, purity)\n\tcase *ast.TypeAssertExpr:\n\t\treturn MayHaveSideEffects(pass, expr.X, purity)\n\tcase *ast.UnaryExpr:\n\t\tif MayHaveSideEffects(pass, expr.X, purity) {\n\t\t\treturn true\n\t\t}\n\t\treturn expr.Op == token.ARROW || expr.Op == token.AND\n\tcase *ast.ParenExpr:\n\t\treturn MayHaveSideEffects(pass, expr.X, purity)\n\tcase nil:\n\t\treturn false\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"internal error: unhandled type %T\", expr))\n\t}\n}\n\n// LanguageVersion returns the version of the Go language that node has access to. This\n// might differ from the version of the Go standard library.\nfunc LanguageVersion(pass *analysis.Pass, node Positioner) string {\n\t// As of Go 1.21, two places can specify the minimum Go version:\n\t// - 'go' directives in go.mod and go.work files\n\t// - individual files by using '//go:build'\n\t//\n\t// Individual files can upgrade to a higher version than the module version. Individual files\n\t// can also downgrade to a lower version, but only if the module version is at least Go 1.21.\n\t//\n\t// The restriction on downgrading doesn't matter to us. All language changes before Go 1.22 will\n\t// not type-check on versions that are too old, and thus never reach our analyzes. In practice,\n\t// such ineffective downgrading will always be useless, as the compiler will not restrict the\n\t// language features used, and doesn't ever rely on minimum versions to restrict the use of the\n\t// standard library. However, for us, both choices (respecting or ignoring ineffective\n\t// downgrading) have equal complexity, but only respecting it has a non-zero chance of reducing\n\t// noisy positives.\n\t//\n\t// The minimum Go versions are exposed via go/ast.File.GoVersion and go/types.Package.GoVersion.\n\t// ast.File's version is populated by the parser, whereas types.Package's version is populated\n\t// from the Go version specified in the types.Config, which is set by our package loader, based\n\t// on the module information provided by go/packages, via 'go list -json'.\n\t//\n\t// As of Go 1.21, standard library packages do not present themselves as modules, and thus do\n\t// not have a version set on their types.Package. In this case, we fall back to the version\n\t// provided by our '-go' flag. In most cases, '-go' defaults to 'module', which falls back to\n\t// the Go version that Staticcheck was built with when no module information exists. In the\n\t// future, the standard library will hopefully be a proper module (see\n\t// https://github.com/golang/go/issues/61174#issuecomment-1622471317). In that case, the version\n\t// of standard library packages will match that of the used Go version. At that point,\n\t// Staticcheck will refuse to work with Go versions that are too new, to avoid misinterpreting\n\t// code due to language changes.\n\t//\n\t// We also lack module information when building in GOPATH mode. In this case, the implied\n\t// language version is at most Go 1.21, as per https://github.com/golang/go/issues/60915. We\n\t// don't handle this yet, and it will not matter until Go 1.22.\n\t//\n\t// It is not clear how per-file downgrading behaves in GOPATH mode. On the one hand, no module\n\t// version at all is provided, which should preclude per-file downgrading. On the other hand,\n\t// https://github.com/golang/go/issues/60915 suggests that the language version is at most 1.21\n\t// in GOPATH mode, which would allow per-file downgrading. Again it doesn't affect us, as all\n\t// relevant language changes before Go 1.22 will lead to type-checking failures and never reach\n\t// us.\n\t//\n\t// Per-file upgrading is permitted in GOPATH mode.\n\n\t// If the file has its own Go version, we will return that. Otherwise, we default to\n\t// the type checker's GoVersion, which is populated from either the Go module, or from\n\t// our '-go' flag.\n\treturn pass.TypesInfo.FileVersions[File(pass, node)]\n}\n\n// StdlibVersion returns the version of the Go standard library that node can expect to\n// have access to. This might differ from the language version for versions of Go older\n// than 1.21.\nfunc StdlibVersion(pass *analysis.Pass, node Positioner) string {\n\t// The Go version as specified in go.mod or via the '-go' flag\n\tn := pass.Pkg.GoVersion()\n\n\tf := File(pass, node)\n\tif f == nil {\n\t\tpanic(fmt.Sprintf(\"no file found for node with position %s\", pass.Fset.PositionFor(node.Pos(), false)))\n\t}\n\n\tif nf := f.GoVersion; nf != \"\" {\n\t\tif version.Compare(n, \"go1.21\") == -1 {\n\t\t\t// Before Go 1.21, the Go version set in go.mod specified the maximum language\n\t\t\t// version available to the module. It wasn't uncommon to set the version to\n\t\t\t// Go 1.20 but restrict usage of 1.20 functionality (both language and stdlib)\n\t\t\t// to files tagged for 1.20, and supporting a lower version overall. As such,\n\t\t\t// a file tagged lower than the module version couldn't expect to have access\n\t\t\t// to the standard library of the version set in go.mod.\n\t\t\t//\n\t\t\t// At the same time, a file tagged higher than the module version, while not\n\t\t\t// able to use newer language features, would still have been able to use a\n\t\t\t// newer standard library.\n\t\t\t//\n\t\t\t// While Go 1.21's behavior has been backported to 1.19.11 and 1.20.6, users'\n\t\t\t// expectations have not.\n\t\t\treturn nf\n\t\t} else {\n\t\t\t// Go 1.21 and newer refuse to build modules that depend on versions newer\n\t\t\t// than the used version of the Go toolchain. This means that in a 1.22 module\n\t\t\t// with a file tagged as 1.17, the file can expect to have access to 1.22's\n\t\t\t// standard library (but not to 1.22 language features). A file tagged with a\n\t\t\t// version higher than the minimum version has access to the newer standard\n\t\t\t// library (and language features.)\n\t\t\t//\n\t\t\t// Do note that strictly speaking we're conflating the Go version and the\n\t\t\t// module version in our check. Nothing is stopping a user from using Go 1.17\n\t\t\t// (which didn't implement the new rules for versions in go.mod) to build a Go\n\t\t\t// 1.22 module, in which case a file tagged with go1.17 will not have access to the 1.22\n\t\t\t// standard library. However, we believe that if a module requires 1.21 or\n\t\t\t// newer, then the author clearly expects the new behavior, and doesn't care\n\t\t\t// for the old one. Otherwise they would've specified an older version.\n\t\t\t//\n\t\t\t// In other words, the module version also specifies what it itself actually means, with\n\t\t\t// >=1.21 being a minimum version for the toolchain, and <1.21 being a maximum version for\n\t\t\t// the language.\n\n\t\t\tif version.Compare(nf, n) == 1 {\n\t\t\t\treturn nf\n\t\t\t}\n\t\t}\n\t}\n\n\treturn n\n}\n\nvar integerLiteralQ = pattern.MustParse(`(IntegerLiteral tv)`)\n\nfunc IntegerLiteral(pass *analysis.Pass, node ast.Node) (types.TypeAndValue, bool) {\n\tm, ok := Match(pass, integerLiteralQ, node)\n\tif !ok {\n\t\treturn types.TypeAndValue{}, false\n\t}\n\treturn m.State[\"tv\"].(types.TypeAndValue), true\n}\n\nfunc IsIntegerLiteral(pass *analysis.Pass, node ast.Node, value constant.Value) bool {\n\ttv, ok := IntegerLiteral(pass, node)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn constant.Compare(tv.Value, token.EQL, value)\n}\n\n// IsMethod reports whether expr is a method call of a named method with signature meth.\n// If name is empty, it is not checked.\n// For now, method expressions (Type.Method(recv, ..)) are not considered method calls.\nfunc IsMethod(pass *analysis.Pass, expr *ast.SelectorExpr, name string, meth *types.Signature) bool {\n\tif name != \"\" && expr.Sel.Name != name {\n\t\treturn false\n\t}\n\tsel, ok := pass.TypesInfo.Selections[expr]\n\tif !ok || sel.Kind() != types.MethodVal {\n\t\treturn false\n\t}\n\treturn types.Identical(sel.Type(), meth)\n}\n\nfunc RefersTo(pass *analysis.Pass, expr ast.Expr, ident types.Object) bool {\n\tfound := false\n\tfn := func(node ast.Node) bool {\n\t\tident2, ok := node.(*ast.Ident)\n\t\tif !ok {\n\t\t\treturn true\n\t\t}\n\t\tif ident == pass.TypesInfo.ObjectOf(ident2) {\n\t\t\tfound = true\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t}\n\tast.Inspect(expr, fn)\n\treturn found\n}\n"
  },
  {
    "path": "analysis/code/code_test.go",
    "content": "package code\n\nimport \"testing\"\n\nvar constraintsFromNameTests = []struct {\n\tin  string\n\tout string\n}{\n\t{\"foo.go\", \"\"},\n\t{\"foo_windows.go\", \"windows\"},\n\t{\"foo_unix.go\", \"\"},\n\t{\"foo_windows_amd64.go\", \"windows && amd64\"},\n\t{\"foo_amd64.go\", \"amd64\"},\n\t{\"foo_windows_nonsense.go\", \"\"},\n\t{\"foo_nonsense_amd64.go\", \"amd64\"},\n\t{\"foo_nonsense_windows.go\", \"windows\"},\n\t{\"foo_nonsense_windows_amd64.go\", \"amd64\"},\n\t{\"foo_windows_test.go\", \"windows\"},\n\t{\"linux.go\", \"\"},\n\t{\"linux_amd64.go\", \"amd64\"},\n\t{\"amd64_linux.go\", \"linux\"},\n\t{\"amd64.go\", \"\"},\n}\n\nfunc TestConstraintsFromName(t *testing.T) {\n\tfor _, tc := range constraintsFromNameTests {\n\t\texpr := constraintsFromName(tc.in)\n\t\tvar out string\n\t\tif expr != nil {\n\t\t\tout = expr.String()\n\t\t}\n\t\tif out != tc.out {\n\t\t\tt.Errorf(\"constraintsFromName(%q) == %q, expected %q\", tc.in, out, tc.out)\n\t\t}\n\t}\n}\n\nfunc FuzzConstraintsFromName(f *testing.F) {\n\tfor _, tc := range constraintsFromNameTests {\n\t\tf.Add(tc.in)\n\t}\n\n\tf.Fuzz(func(t *testing.T, name string) {\n\t\tconstraintsFromName(name)\n\t})\n}\n"
  },
  {
    "path": "analysis/code/visit.go",
    "content": "package code\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/format\"\n\t\"go/types\"\n\t\"iter\"\n\t\"slices\"\n\n\ttypeindexanalyzer \"honnef.co/go/tools/internal/analysisinternal/typeindex\"\n\t\"honnef.co/go/tools/internal/typesinternal/typeindex\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n)\n\nvar RequiredAnalyzers = []*analysis.Analyzer{inspect.Analyzer, typeindexanalyzer.Analyzer}\n\nfunc Cursor(pass *analysis.Pass) inspector.Cursor {\n\treturn pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Root()\n}\n\nfunc Preorder(pass *analysis.Pass, fn func(ast.Node), types ...ast.Node) {\n\tpass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder(types, fn)\n}\n\nfunc PreorderStack(pass *analysis.Pass, fn func(ast.Node, []ast.Node), types ...ast.Node) {\n\tpass.ResultOf[inspect.Analyzer].(*inspector.Inspector).WithStack(types, func(n ast.Node, push bool, stack []ast.Node) (proceed bool) {\n\t\tif push {\n\t\t\tfn(n, stack)\n\t\t}\n\t\treturn true\n\t})\n}\n\nfunc Matches(pass *analysis.Pass, qs ...pattern.Pattern) iter.Seq2[ast.Node, *pattern.Matcher] {\n\treturn func(yield func(ast.Node, *pattern.Matcher) bool) {\n\t\tfor _, q := range qs {\n\t\t\tif !CouldMatchAny(pass, q) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif len(q.RootCallSymbols) != 0 {\n\t\t\t\tindex := pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index)\n\t\t\t\tfor _, isym := range q.RootCallSymbols {\n\t\t\t\t\tvar obj types.Object\n\t\t\t\t\tif isym.Type == \"\" {\n\t\t\t\t\t\tobj = index.Object(isym.Path, isym.Ident)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tobj = index.Selection(isym.Path, isym.Type, isym.Ident)\n\t\t\t\t\t}\n\t\t\t\t\tfor c := range index.Calls(obj) {\n\t\t\t\t\t\tnode := c.Node()\n\t\t\t\t\t\tif m, ok := Match(pass, q, node); ok {\n\t\t\t\t\t\t\tif !yield(node, m) {\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tins := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)\n\t\t\t\tfn := func(node ast.Node, push bool) bool {\n\t\t\t\t\tif !push {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\n\t\t\t\t\tif m, ok := Match(pass, q, node); ok {\n\t\t\t\t\t\treturn yield(node, m)\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tins.Nodes(q.EntryNodes, fn)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc Match(pass *analysis.Pass, q pattern.Pattern, node ast.Node) (*pattern.Matcher, bool) {\n\t// Note that we ignore q.Relevant – callers of Match usually use\n\t// AST inspectors that already filter on nodes we're interested\n\t// in.\n\tm := &pattern.Matcher{TypesInfo: pass.TypesInfo}\n\tok := m.Match(q, node)\n\treturn m, ok\n}\n\nfunc CouldMatchAny(pass *analysis.Pass, qs ...pattern.Pattern) bool {\n\tindex := pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index)\n\tvar do func(node pattern.Node) bool\n\tdo = func(node pattern.Node) bool {\n\t\tswitch node := node.(type) {\n\t\tcase pattern.Any:\n\t\t\treturn true\n\t\tcase pattern.Or:\n\t\t\treturn slices.ContainsFunc(node.Nodes, do)\n\t\tcase pattern.And:\n\t\t\tfor _, child := range node.Nodes {\n\t\t\t\tif !do(child) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\tcase pattern.IndexSymbol:\n\t\t\tif node.Type == \"\" {\n\t\t\t\treturn index.Object(node.Path, node.Ident) != nil\n\t\t\t} else {\n\t\t\t\treturn index.Selection(node.Path, node.Type, node.Ident) != nil\n\t\t\t}\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"internal error: unexpected type %T\", node))\n\t\t}\n\t}\n\n\tfor _, q := range qs {\n\t\tif do(q.SymbolsPattern) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc MatchAndEdit(pass *analysis.Pass, before, after pattern.Pattern, node ast.Node) (*pattern.Matcher, []analysis.TextEdit, bool) {\n\tm, ok := Match(pass, before, node)\n\tif !ok {\n\t\treturn m, nil, false\n\t}\n\tr := pattern.NodeToAST(after.Root, m.State)\n\tbuf := &bytes.Buffer{}\n\tformat.Node(buf, pass.Fset, r)\n\tedit := []analysis.TextEdit{{\n\t\tPos:     node.Pos(),\n\t\tEnd:     node.End(),\n\t\tNewText: buf.Bytes(),\n\t}}\n\treturn m, edit, true\n}\n\nfunc EditMatch(pass *analysis.Pass, node ast.Node, m *pattern.Matcher, after pattern.Pattern) []analysis.TextEdit {\n\tr := pattern.NodeToAST(after.Root, m.State)\n\tbuf := &bytes.Buffer{}\n\tformat.Node(buf, pass.Fset, r)\n\tedit := []analysis.TextEdit{{\n\t\tPos:     node.Pos(),\n\t\tEnd:     node.End(),\n\t\tNewText: buf.Bytes(),\n\t}}\n\treturn edit\n}\n"
  },
  {
    "path": "analysis/dfa/dfa.el",
    "content": "(require 'cl-lib)\n\n(defun format-state (prefix state ⊤ ⊥)\n  (cond ((string= state \"⊥\") ⊥)\n\t\t((string= state \"⊤\") ⊤)\n\t\t(t (format \"%s%s\" prefix state))))\n\n(defun dh/orgtbl-to-dfa-binary-table (table params)\n  (let* ((table (--filter (not (equal 'hline it)) table))\n\t\t (rows (1- (length table)))\n\t\t (cols (1- (length (nth 0 table))))\n\t\t (prefix (plist-get params :prefix))\n\t\t (var (plist-get params :var))\n\t\t (⊤ (plist-get params :⊤))\n\t\t (⊥ (plist-get params :⊥)))\n\n\t(concat\n\t (if var (concat \"var \" var \" = \") \"\")\n\t (format\n\t  \"dfa.BinaryTable(%s, map[[2]%s]%s{\\n\"\n\t  ⊤ prefix prefix)\n\t (mapconcat\n\t  (lambda (rowIdx)\n\t\t(mapconcat\n\t\t (lambda (colIdx)\n\t\t   (let* ((x (nth 0 (nth rowIdx table)))\n\t\t\t\t  (y (nth colIdx (nth 0 table)))\n\t\t\t\t  (z (nth colIdx (nth rowIdx table))))\n\t\t\t (format \"{%s, %s}: %s,\" (format-state prefix x ⊤ ⊥) (format-state prefix y ⊤ ⊥) (format-state prefix z ⊤ ⊥))))\n\t\t (number-sequence 1 cols)\n\t\t \"\\n\"))\n\t  (number-sequence 1 rows)\n\t  \"\\n\\n\")\n\t \"\\n})\")))\n"
  },
  {
    "path": "analysis/dfa/dfa.go",
    "content": "// Package dfa provides types and functions for implementing data-flow analyses.\npackage dfa\n\nimport (\n\t\"cmp\"\n\t\"fmt\"\n\t\"log\"\n\t\"math/bits\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"golang.org/x/exp/constraints\"\n\t\"honnef.co/go/tools/go/ir\"\n)\n\nconst debugging = false\n\nfunc debugf(f string, args ...any) {\n\tif debugging {\n\t\tlog.Printf(f, args...)\n\t}\n}\n\n// Join defines the [∨] operation for a [join-semilattice]. It must implement a commutative and associative binary operation\n// that returns the least upper bound of two states from S.\n//\n// Code that calls Join functions is expected to handle the [⊥ and ⊤ elements], as well as implement idempotency. That is,\n// the following properties will be enforced:\n//\n//   - x ∨ ⊥ = x\n//   - x ∨ ⊤ = ⊤\n//   - x ∨ x = x\n//\n// Simple table-based join functions can be created using [JoinTable].\n//\n// [∨]: https://en.wikipedia.org/wiki/Join_and_meet\n// [join-semilattice]: https://en.wikipedia.org/wiki/Semilattice\n// [⊥ and ⊤ elements]: https://en.wikipedia.org/wiki/Greatest_element_and_least_element#Top_and_bottom\ntype Join[S comparable] func(S, S) S\n\n// Mapping maps a single [ir.Value] to an abstract state.\ntype Mapping[S comparable] struct {\n\tValue    ir.Value\n\tState    S\n\tDecision Decision\n}\n\n// Decision describes how a mapping from an [ir.Value] to an abstract state came to be.\n// Decisions are provided by transfer functions when they create mappings.\ntype Decision struct {\n\t// The relevant values that the transfer function used to make the decision.\n\tInputs []ir.Value\n\t// A human-readable description of the decision.\n\tDescription string\n\t// Whether this is the source of an abstract state. For example, in a taint analysis, the call to a function that\n\t// produces a tainted value would be the source of the taint state, and any instructions that operate on\n\t// and propagate tainted values would not be sources.\n\tSource bool\n}\n\nfunc (m Mapping[S]) String() string {\n\treturn fmt.Sprintf(\"%s = %v\", m.Value.Name(), m.State)\n}\n\n// M is a helper for constructing instances of [Mapping].\nfunc M[S comparable](v ir.Value, s S, d Decision) Mapping[S] {\n\treturn Mapping[S]{Value: v, State: s, Decision: d}\n}\n\n// Ms is a helper for constructing slices of mappings.\n//\n// Example:\n//\n//\tMs(M(v1, d1, ...), M(v2, d2, ...))\nfunc Ms[S comparable](ms ...Mapping[S]) []Mapping[S] {\n\treturn ms\n}\n\n// Framework describes a monotone data-flow framework ⟨S, ∨, Transfer⟩ using a bounded join-semilattice ⟨S, ∨⟩ and a\n// monotonic transfer function.\n//\n// Transfer implements the transfer function. Given an instruction, it should return zero or more mappings from IR\n// values to abstract values, i.e. values from the semilattice. Transfer must be monotonic. ϕ instructions are handled\n// automatically and do not cause Transfer to be called.\n//\n// The set S is defined implicitly by the values returned by Join and Transfer and needn't be finite. In addition, it\n// contains the elements ⊥ and ⊤ (Bottom and Top) with Join(x, ⊥) = x and Join(x, ⊤) = ⊤. The provided Join function is\n// wrapped to handle these elements automatically. All IR values start in the ⊥ state.\n//\n// Abstract states are associated with IR values. As such, the analysis is sparse and favours the partitioned variable\n// lattice (PVL) property.\ntype Framework[S comparable] struct {\n\tJoin     Join[S]\n\tTransfer func(*Instance[S], ir.Instruction) []Mapping[S]\n\tBottom   S\n\tTop      S\n}\n\n// Start returns a new instance of the framework. See also [Framework.Forward].\nfunc (fw *Framework[S]) Start() *Instance[S] {\n\tif fw.Bottom == fw.Top {\n\t\tpanic(\"framework's ⊥ and ⊤ are identical; did you forget to specify them?\")\n\t}\n\n\treturn &Instance[S]{\n\t\tFramework: fw,\n\t\tMapping:   map[ir.Value]Mapping[S]{},\n\t}\n}\n\n// Forward runs an intraprocedural forward data flow analysis, using an iterative fixed-point algorithm, given the\n// functions specified in the framework. It combines [Framework.Start] and [Instance.Forward].\nfunc (fw *Framework[S]) Forward(fn *ir.Function) *Instance[S] {\n\tins := fw.Start()\n\tins.Forward(fn)\n\treturn ins\n}\n\n// Dot returns a directed graph in [Graphviz] format that represents the finite join-semilattice ⟨S, ≤⟩.\n// Vertices represent elements in S and edges represent the ≤ relation between elements.\n// We map from ⟨S, ∨⟩ to ⟨S, ≤⟩ by computing x ∨ y for all elements in [S]², where x ≤ y iff x ∨ y == y.\n//\n// The resulting graph can be filtered through [tred] to compute the transitive reduction of the graph, the\n// visualisation of which corresponds to the Hasse diagram of the semilattice.\n//\n// The set of states should not include the ⊥ and ⊤ elements.\n//\n// [Graphviz]: https://graphviz.org/\n// [tred]: https://graphviz.org/docs/cli/tred/\nfunc Dot[S comparable](fn Join[S], states []S, bottom, top S) string {\n\tvar sb strings.Builder\n\tsb.WriteString(\"digraph{\\n\")\n\tsb.WriteString(\"rankdir=\\\"BT\\\"\\n\")\n\n\tfor i, v := range states {\n\t\tif vs, ok := any(v).(fmt.Stringer); ok {\n\t\t\tfmt.Fprintf(&sb, \"n%d [label=%q]\\n\", i, vs)\n\t\t} else {\n\t\t\tfmt.Fprintf(&sb, \"n%d [label=%q]\\n\", i, fmt.Sprintf(\"%v\", v))\n\t\t}\n\t}\n\n\tfor dx, x := range states {\n\t\tfor dy, y := range states {\n\t\t\tif dx == dy {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif join(fn, x, y, bottom, top) == y {\n\t\t\t\tfmt.Fprintf(&sb, \"n%d -> n%d\\n\", dx, dy)\n\t\t\t}\n\t\t}\n\t}\n\n\tsb.WriteString(\"}\")\n\treturn sb.String()\n}\n\n// Instance is an instance of a data-flow analysis. It is created by [Framework.Forward].\ntype Instance[S comparable] struct {\n\tFramework *Framework[S]\n\t// Mapping is the result of the analysis. Consider using Instance.Value instead of accessing Mapping\n\t// directly, as it correctly returns ⊥ for missing values.\n\tMapping map[ir.Value]Mapping[S]\n}\n\n// Set maps v to the abstract value d. It does not apply any checks. This should only be used before calling [Instance.Forward], to set\n// initial states of values.\nfunc (ins *Instance[S]) Set(v ir.Value, d S) {\n\tins.Mapping[v] = Mapping[S]{Value: v, State: d}\n}\n\n// Value returns the abstract value for v. If none was set, it returns ⊥.\nfunc (ins *Instance[S]) Value(v ir.Value) S {\n\tm, ok := ins.Mapping[v]\n\tif ok {\n\t\treturn m.State\n\t} else {\n\t\treturn ins.Framework.Bottom\n\t}\n}\n\n// Decision returns the decision of the mapping for v, if any.\nfunc (ins *Instance[S]) Decision(v ir.Value) Decision {\n\treturn ins.Mapping[v].Decision\n}\n\nvar dfsDebugMu sync.Mutex\n\nfunc join[S comparable](fn Join[S], a, b, bottom, top S) S {\n\tswitch {\n\tcase a == top || b == top:\n\t\treturn top\n\tcase a == bottom:\n\t\treturn b\n\tcase b == bottom:\n\t\treturn a\n\tcase a == b:\n\t\treturn a\n\tdefault:\n\t\treturn fn(a, b)\n\t}\n}\n\n// Forward runs a forward data-flow analysis on fn.\nfunc (ins *Instance[S]) Forward(fn *ir.Function) {\n\tif debugging {\n\t\tdfsDebugMu.Lock()\n\t\tdefer dfsDebugMu.Unlock()\n\t}\n\n\tdebugf(\"Analyzing %s\\n\", fn)\n\tif ins.Mapping == nil {\n\t\tins.Mapping = map[ir.Value]Mapping[S]{}\n\t}\n\n\tworklist := map[ir.Instruction]struct{}{}\n\tfor _, b := range fn.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tworklist[instr] = struct{}{}\n\t\t}\n\t}\n\tfor len(worklist) > 0 {\n\t\tvar instr ir.Instruction\n\t\tfor instr = range worklist {\n\t\t\tbreak\n\t\t}\n\t\tdelete(worklist, instr)\n\n\t\tvar ds []Mapping[S]\n\t\tif phi, ok := instr.(*ir.Phi); ok {\n\t\t\td := ins.Framework.Bottom\n\t\t\tfor _, edge := range phi.Edges {\n\t\t\t\ta, b := d, ins.Value(edge)\n\t\t\t\td = join(ins.Framework.Join, a, b, ins.Framework.Bottom, ins.Framework.Top)\n\t\t\t\tdebugf(\"join(%v, %v) = %v\", a, b, d)\n\t\t\t}\n\t\t\tds = []Mapping[S]{{Value: phi, State: d, Decision: Decision{Inputs: phi.Edges, Description: \"this variable merges the results of multiple branches\"}}}\n\t\t} else {\n\t\t\tds = ins.Framework.Transfer(ins, instr)\n\t\t}\n\t\tif len(ds) > 0 {\n\t\t\tif v, ok := instr.(ir.Value); ok {\n\t\t\t\tdebugf(\"transfer(%s = %s) = %v\", v.Name(), instr, ds)\n\t\t\t} else {\n\t\t\t\tdebugf(\"transfer(%s) = %v\", instr, ds)\n\t\t\t}\n\t\t}\n\t\tfor i, d := range ds {\n\t\t\told := ins.Value(d.Value)\n\t\t\tdd := d.State\n\t\t\tif dd != old {\n\t\t\t\tif j := join(ins.Framework.Join, old, dd, ins.Framework.Bottom, ins.Framework.Top); j != dd {\n\t\t\t\t\tpanic(fmt.Sprintf(\"transfer function isn't monotonic; Transfer(%v)[%d] = %v; join(%v, %v) = %v\", instr, i, dd, old, dd, j))\n\t\t\t\t}\n\t\t\t\tins.Mapping[d.Value] = Mapping[S]{Value: d.Value, State: dd, Decision: d.Decision}\n\n\t\t\t\tfor _, ref := range *instr.Referrers() {\n\t\t\t\t\tworklist[ref] = struct{}{}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tprintMapping(fn, ins.Mapping)\n\t}\n}\n\n// Propagate is a helper for creating a [Mapping] that propagates the abstract state of src to dst.\n// The desc parameter is used as the value of Decision.Description.\nfunc (ins *Instance[S]) Propagate(dst, src ir.Value, desc string) Mapping[S] {\n\treturn M(dst, ins.Value(src), Decision{Inputs: []ir.Value{src}, Description: desc})\n}\n\nfunc (ins *Instance[S]) Transform(dst ir.Value, s S, src ir.Value, desc string) Mapping[S] {\n\treturn M(dst, s, Decision{Inputs: []ir.Value{src}, Description: desc})\n}\n\nfunc printMapping[S any](fn *ir.Function, m map[ir.Value]S) {\n\tif !debugging {\n\t\treturn\n\t}\n\n\tdebugf(\"Mapping for %s:\\n\", fn)\n\tvar keys []ir.Value\n\tfor k := range m {\n\t\tkeys = append(keys, k)\n\t}\n\tslices.SortFunc(keys, func(a, b ir.Value) int {\n\t\treturn cmp.Compare(a.ID(), b.ID())\n\t})\n\tfor _, k := range keys {\n\t\tv := m[k]\n\t\tdebugf(\"\\t%v\\n\", v)\n\t}\n}\n\n// BinaryTable returns a binary operator based on the provided mapping.\n// For missing pairs of values, the default value will be returned.\nfunc BinaryTable[S comparable](default_ S, m map[[2]S]S) func(S, S) S {\n\treturn func(a, b S) S {\n\t\tif d, ok := m[[2]S{a, b}]; ok {\n\t\t\treturn d\n\t\t} else if d, ok := m[[2]S{b, a}]; ok {\n\t\t\treturn d\n\t\t} else {\n\t\t\treturn default_\n\t\t}\n\t}\n}\n\n// JoinTable returns a [Join] function based on the provided mapping.\n// For missing pairs of values, the default value will be returned.\nfunc JoinTable[S comparable](top S, m map[[2]S]S) Join[S] {\n\treturn func(a, b S) S {\n\t\tif d, ok := m[[2]S{a, b}]; ok {\n\t\t\treturn d\n\t\t} else if d, ok := m[[2]S{b, a}]; ok {\n\t\t\treturn d\n\t\t} else {\n\t\t\treturn top\n\t\t}\n\t}\n}\n\nfunc PowerSet[S constraints.Integer](all S) []S {\n\tout := make([]S, all+1)\n\tfor i := range out {\n\t\tout[i] = S(i)\n\t}\n\treturn out\n}\n\nfunc MapSet[S constraints.Integer](set S, fn func(S) S) S {\n\tbits := 64 - bits.LeadingZeros64(uint64(set))\n\tvar out S\n\tfor i := range bits {\n\t\tif b := (set & (1 << i)); b != 0 {\n\t\t\tout |= fn(b)\n\t\t}\n\t}\n\treturn out\n}\n\nfunc MapCartesianProduct[S constraints.Integer](x, y S, fn func(S, S) S) S {\n\tbitsX := 64 - bits.LeadingZeros64(uint64(x))\n\tbitsY := 64 - bits.LeadingZeros64(uint64(y))\n\n\tvar out S\n\tfor i := range bitsX {\n\t\tfor j := range bitsY {\n\t\t\tbx := x & (1 << i)\n\t\t\tby := y & (1 << j)\n\n\t\t\tif bx != 0 && by != 0 {\n\t\t\t\tout |= fn(bx, by)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out\n}\n"
  },
  {
    "path": "analysis/edit/edit.go",
    "content": "// Package edit contains helpers for creating suggested fixes.\npackage edit\n\nimport (\n\t\"bytes\"\n\t\"go/ast\"\n\t\"go/format\"\n\t\"go/token\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"honnef.co/go/tools/pattern\"\n)\n\n// Ranger describes values that have a start and end position.\n// In most cases these are either ast.Node or manually constructed ranges.\ntype Ranger interface {\n\tPos() token.Pos\n\tEnd() token.Pos\n}\n\n// Range implements the Ranger interface.\ntype Range [2]token.Pos\n\nfunc (r Range) Pos() token.Pos { return r[0] }\nfunc (r Range) End() token.Pos { return r[1] }\n\n// ReplaceWithString replaces a range with a string.\nfunc ReplaceWithString(old Ranger, new string) analysis.TextEdit {\n\treturn analysis.TextEdit{\n\t\tPos:     old.Pos(),\n\t\tEnd:     old.End(),\n\t\tNewText: []byte(new),\n\t}\n}\n\n// ReplaceWithNode replaces a range with an AST node.\nfunc ReplaceWithNode(fset *token.FileSet, old Ranger, new ast.Node) analysis.TextEdit {\n\tbuf := &bytes.Buffer{}\n\tif err := format.Node(buf, fset, new); err != nil {\n\t\tpanic(\"internal error: \" + err.Error())\n\t}\n\treturn analysis.TextEdit{\n\t\tPos:     old.Pos(),\n\t\tEnd:     old.End(),\n\t\tNewText: buf.Bytes(),\n\t}\n}\n\n// ReplaceWithPattern replaces a range with the result of executing a pattern.\nfunc ReplaceWithPattern(fset *token.FileSet, old Ranger, new pattern.Pattern, state pattern.State) analysis.TextEdit {\n\tr := pattern.NodeToAST(new.Root, state)\n\tbuf := &bytes.Buffer{}\n\tformat.Node(buf, fset, r)\n\treturn analysis.TextEdit{\n\t\tPos:     old.Pos(),\n\t\tEnd:     old.End(),\n\t\tNewText: buf.Bytes(),\n\t}\n}\n\n// Delete deletes a range of code.\nfunc Delete(old Ranger) analysis.TextEdit {\n\treturn analysis.TextEdit{\n\t\tPos:     old.Pos(),\n\t\tEnd:     old.End(),\n\t\tNewText: nil,\n\t}\n}\n\nfunc Fix(msg string, edits ...analysis.TextEdit) analysis.SuggestedFix {\n\treturn analysis.SuggestedFix{\n\t\tMessage:   msg,\n\t\tTextEdits: edits,\n\t}\n}\n\n// Selector creates a new selector expression.\nfunc Selector(x, sel string) *ast.SelectorExpr {\n\treturn &ast.SelectorExpr{\n\t\tX:   &ast.Ident{Name: x},\n\t\tSel: &ast.Ident{Name: sel},\n\t}\n}\n"
  },
  {
    "path": "analysis/facts/deprecated/deprecated.go",
    "content": "package deprecated\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\ntype IsDeprecated struct{ Msg string }\n\nfunc (*IsDeprecated) AFact()           {}\nfunc (d *IsDeprecated) String() string { return \"Deprecated: \" + d.Msg }\n\ntype Result struct {\n\tObjects  map[types.Object]*IsDeprecated\n\tPackages map[*types.Package]*IsDeprecated\n}\n\nvar Analyzer = &analysis.Analyzer{\n\tName:       \"fact_deprecated\",\n\tDoc:        \"Mark deprecated objects\",\n\tRun:        deprecated,\n\tFactTypes:  []analysis.Fact{(*IsDeprecated)(nil)},\n\tResultType: reflect.TypeFor[Result](),\n}\n\nfunc deprecated(pass *analysis.Pass) (any, error) {\n\tvar names []*ast.Ident\n\n\textractDeprecatedMessage := func(docs []*ast.CommentGroup) string {\n\t\tfor _, doc := range docs {\n\t\t\tif doc == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tparts := strings.SplitSeq(doc.Text(), \"\\n\\n\")\n\t\t\tfor part := range parts {\n\t\t\t\tif !strings.HasPrefix(part, \"Deprecated: \") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\talt := part[len(\"Deprecated: \"):]\n\t\t\t\talt = strings.Replace(alt, \"\\n\", \" \", -1)\n\t\t\t\treturn alt\n\t\t\t}\n\t\t}\n\t\treturn \"\"\n\t}\n\n\tdoDocs := func(names []*ast.Ident, docs []*ast.CommentGroup) {\n\t\talt := extractDeprecatedMessage(docs)\n\t\tif alt == \"\" {\n\t\t\treturn\n\t\t}\n\n\t\tfor _, name := range names {\n\t\t\tobj := pass.TypesInfo.ObjectOf(name)\n\t\t\tpass.ExportObjectFact(obj, &IsDeprecated{alt})\n\t\t}\n\t}\n\n\tvar docs []*ast.CommentGroup\n\tfor _, f := range pass.Files {\n\t\tdocs = append(docs, f.Doc)\n\t}\n\tif alt := extractDeprecatedMessage(docs); alt != \"\" {\n\t\t// Don't mark package syscall as deprecated, even though\n\t\t// it is. A lot of people still use it for simple\n\t\t// constants like SIGKILL, and I am not comfortable\n\t\t// telling them to use x/sys for that.\n\t\tif pass.Pkg.Path() != \"syscall\" {\n\t\t\tpass.ExportPackageFact(&IsDeprecated{alt})\n\t\t}\n\t}\n\n\tdocs = docs[:0]\n\tfor _, f := range pass.Files {\n\t\tfn := func(node ast.Node) bool {\n\t\t\tif node == nil {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tvar ret bool\n\t\t\tswitch node := node.(type) {\n\t\t\tcase *ast.GenDecl:\n\t\t\t\tswitch node.Tok {\n\t\t\t\tcase token.TYPE, token.CONST, token.VAR:\n\t\t\t\t\tdocs = append(docs, node.Doc)\n\t\t\t\t\tfor i := range node.Specs {\n\t\t\t\t\t\tswitch n := node.Specs[i].(type) {\n\t\t\t\t\t\tcase *ast.ValueSpec:\n\t\t\t\t\t\t\tnames = append(names, n.Names...)\n\t\t\t\t\t\tcase *ast.TypeSpec:\n\t\t\t\t\t\t\tnames = append(names, n.Name)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tret = true\n\t\t\t\tdefault:\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\tcase *ast.FuncDecl:\n\t\t\t\tdocs = append(docs, node.Doc)\n\t\t\t\tnames = []*ast.Ident{node.Name}\n\t\t\t\tret = false\n\t\t\tcase *ast.TypeSpec:\n\t\t\t\tdocs = append(docs, node.Doc)\n\t\t\t\tnames = []*ast.Ident{node.Name}\n\t\t\t\tret = true\n\t\t\tcase *ast.ValueSpec:\n\t\t\t\tdocs = append(docs, node.Doc)\n\t\t\t\tnames = node.Names\n\t\t\t\tret = false\n\t\t\tcase *ast.File:\n\t\t\t\treturn true\n\t\t\tcase *ast.StructType:\n\t\t\t\tfor _, field := range node.Fields.List {\n\t\t\t\t\tdoDocs(field.Names, []*ast.CommentGroup{field.Doc})\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\tcase *ast.InterfaceType:\n\t\t\t\tfor _, field := range node.Methods.List {\n\t\t\t\t\tdoDocs(field.Names, []*ast.CommentGroup{field.Doc})\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif len(names) == 0 || len(docs) == 0 {\n\t\t\t\treturn ret\n\t\t\t}\n\t\t\tdoDocs(names, docs)\n\n\t\t\tdocs = docs[:0]\n\t\t\tnames = nil\n\t\t\treturn ret\n\t\t}\n\t\tast.Inspect(f, fn)\n\t}\n\n\tout := Result{\n\t\tObjects:  map[types.Object]*IsDeprecated{},\n\t\tPackages: map[*types.Package]*IsDeprecated{},\n\t}\n\n\tfor _, fact := range pass.AllObjectFacts() {\n\t\tout.Objects[fact.Object] = fact.Fact.(*IsDeprecated)\n\t}\n\tfor _, fact := range pass.AllPackageFacts() {\n\t\tout.Packages[fact.Package] = fact.Fact.(*IsDeprecated)\n\t}\n\n\treturn out, nil\n}\n"
  },
  {
    "path": "analysis/facts/deprecated/deprecated_test.go",
    "content": "package deprecated\n\nimport (\n\t\"testing\"\n\n\t\"golang.org/x/tools/go/analysis/analysistest\"\n)\n\nfunc TestDeprecated(t *testing.T) {\n\tanalysistest.Run(t, analysistest.TestData(), Analyzer, \"example.com/Deprecated\")\n}\n"
  },
  {
    "path": "analysis/facts/deprecated/testdata/src/example.com/Deprecated/Deprecated.go",
    "content": "package pkg\n\n// Deprecated: Don't use this.\nfunc fn2() { // want fn2:`Deprecated: Don't use this\\.`\n}\n\n// This is a function.\n//\n// Deprecated: Don't use this.\n//\n// Here is how you might use it instead.\nfunc fn3() { // want fn3:`Deprecated: Don't use this\\.`\n}\n\n// Handle cases like:\n//\n// Taken from \"os\" package:\n//\n// ```\n// // Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.\n// const (\n// \tSEEK_SET int = 0 // seek relative to the origin of the file\n// \tSEEK_CUR int = 1 // seek relative to the current offset\n// \tSEEK_END int = 2 // seek relative to the end\n// )\n// ```\n//\n// Here all three consts i.e., os.SEEK_SET, os.SEEK_CUR and os.SEEK_END are\n// deprecated and not just os.SEEK_SET.\n\n// Deprecated: Don't use this.\nvar (\n\tSEEK_A = 0 // want SEEK_A:`Deprecated: Don't use this\\.`\n\tSEEK_B = 1 // want SEEK_B:`Deprecated: Don't use this\\.`\n\tSEEK_C = 2 // want SEEK_C:`Deprecated: Don't use this\\.`\n)\n\n// Deprecated: Don't use this.\ntype (\n\tpair struct{ x, y int }    // want pair:`Deprecated: Don't use this\\.`\n\tcube struct{ x, y, z int } // want cube:`Deprecated: Don't use this\\.`\n)\n\n// Deprecated: Don't use this.\nvar SEEK_D = 3 // want SEEK_D:`Deprecated: Don't use this\\.`\nvar SEEK_E = 4\nvar SEEK_F = 5\n"
  },
  {
    "path": "analysis/facts/directives/directives.go",
    "content": "package directives\n\nimport (\n\t\"reflect\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"honnef.co/go/tools/analysis/lint\"\n)\n\nfunc directives(pass *analysis.Pass) (any, error) {\n\treturn lint.ParseDirectives(pass.Files, pass.Fset), nil\n}\n\nvar Analyzer = &analysis.Analyzer{\n\tName:             \"directives\",\n\tDoc:              \"extracts linter directives\",\n\tRun:              directives,\n\tRunDespiteErrors: true,\n\tResultType:       reflect.TypeFor[[]lint.Directive](),\n}\n"
  },
  {
    "path": "analysis/facts/generated/generated.go",
    "content": "package generated\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"io\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\ntype Generator int\n\n// A list of known generators we can detect\nconst (\n\tUnknown Generator = iota\n\tGoyacc\n\tCgo\n\tStringer\n\tProtocGenGo\n)\n\nvar (\n\t// used by cgo before Go 1.11\n\toldCgo = []byte(\"// Created by cgo - DO NOT EDIT\")\n\tprefix = []byte(\"// Code generated \")\n\tsuffix = []byte(\" DO NOT EDIT.\")\n\tnl     = []byte(\"\\n\")\n\tcrnl   = []byte(\"\\r\\n\")\n)\n\nfunc isGenerated(path string) (Generator, bool) {\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn 0, false\n\t}\n\tdefer f.Close()\n\tbr := bufio.NewReader(f)\n\tfor {\n\t\ts, err := br.ReadBytes('\\n')\n\t\tif err != nil && err != io.EOF {\n\t\t\treturn 0, false\n\t\t}\n\t\ts = bytes.TrimSuffix(s, crnl)\n\t\ts = bytes.TrimSuffix(s, nl)\n\t\tif bytes.HasPrefix(s, prefix) && bytes.HasSuffix(s, suffix) {\n\t\t\tif len(s)-len(suffix) < len(prefix) {\n\t\t\t\treturn Unknown, true\n\t\t\t}\n\n\t\t\ttext := string(s[len(prefix) : len(s)-len(suffix)])\n\t\t\tswitch text {\n\t\t\tcase \"by goyacc.\":\n\t\t\t\treturn Goyacc, true\n\t\t\tcase \"by cmd/cgo;\":\n\t\t\t\treturn Cgo, true\n\t\t\tcase \"by protoc-gen-go.\":\n\t\t\t\treturn ProtocGenGo, true\n\t\t\t}\n\t\t\tif strings.HasPrefix(text, `by \"stringer `) {\n\t\t\t\treturn Stringer, true\n\t\t\t}\n\t\t\tif strings.HasPrefix(text, `by goyacc `) {\n\t\t\t\treturn Goyacc, true\n\t\t\t}\n\n\t\t\treturn Unknown, true\n\t\t}\n\t\tif bytes.Equal(s, oldCgo) {\n\t\t\treturn Cgo, true\n\t\t}\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn 0, false\n}\n\nvar Analyzer = &analysis.Analyzer{\n\tName: \"isgenerated\",\n\tDoc:  \"annotate file names that have been code generated\",\n\tRun: func(pass *analysis.Pass) (any, error) {\n\t\tm := map[string]Generator{}\n\t\tfor _, f := range pass.Files {\n\t\t\tpath := pass.Fset.PositionFor(f.Pos(), false).Filename\n\t\t\tg, ok := isGenerated(path)\n\t\t\tif ok {\n\t\t\t\tm[path] = g\n\t\t\t}\n\t\t}\n\t\treturn m, nil\n\t},\n\tRunDespiteErrors: true,\n\tResultType:       reflect.TypeFor[map[string]Generator](),\n}\n"
  },
  {
    "path": "analysis/facts/nilness/nilness.go",
    "content": "package nilness\n\nimport (\n\t\"fmt\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"reflect\"\n\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\n// neverReturnsNilFact denotes that a function's return value will never\n// be nil (typed or untyped). The analysis errs on the side of false\n// negatives.\ntype neverReturnsNilFact struct {\n\tRets []neverNilness\n}\n\nfunc (*neverReturnsNilFact) AFact() {}\nfunc (fact *neverReturnsNilFact) String() string {\n\treturn fmt.Sprintf(\"never returns nil: %v\", fact.Rets)\n}\n\ntype Result struct {\n\tm map[*types.Func][]neverNilness\n}\n\nvar Analysis = &analysis.Analyzer{\n\tName:       \"nilness\",\n\tDoc:        \"Annotates return values that will never be nil (typed or untyped)\",\n\tRun:        run,\n\tRequires:   []*analysis.Analyzer{buildir.Analyzer},\n\tFactTypes:  []analysis.Fact{(*neverReturnsNilFact)(nil)},\n\tResultType: reflect.TypeFor[*Result](),\n}\n\n// MayReturnNil reports whether the ret's return value of fn might be\n// a typed or untyped nil value. The value of ret is zero-based. When\n// globalOnly is true, the only possible nil values are global\n// variables.\n//\n// The analysis has false positives: MayReturnNil can incorrectly\n// report true, but never incorrectly reports false.\nfunc (r *Result) MayReturnNil(fn *types.Func, ret int) (yes bool, globalOnly bool) {\n\tif !typeutil.IsPointerLike(fn.Type().(*types.Signature).Results().At(ret).Type()) {\n\t\treturn false, false\n\t}\n\tif len(r.m[fn]) == 0 {\n\t\treturn true, false\n\t}\n\n\tv := r.m[fn][ret]\n\treturn v != neverNil, v == onlyGlobal\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tseen := map[*ir.Function]struct{}{}\n\tout := &Result{\n\t\tm: map[*types.Func][]neverNilness{},\n\t}\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\timpl(pass, fn, seen)\n\t}\n\n\tfor _, fact := range pass.AllObjectFacts() {\n\t\tout.m[fact.Object.(*types.Func)] = fact.Fact.(*neverReturnsNilFact).Rets\n\t}\n\n\treturn out, nil\n}\n\ntype neverNilness uint8\n\nconst (\n\tneverNil   neverNilness = 1\n\tonlyGlobal neverNilness = 2\n\tnilly      neverNilness = 3\n)\n\nfunc (n neverNilness) String() string {\n\tswitch n {\n\tcase neverNil:\n\t\treturn \"never\"\n\tcase onlyGlobal:\n\t\treturn \"global\"\n\tcase nilly:\n\t\treturn \"nil\"\n\tdefault:\n\t\treturn \"BUG\"\n\t}\n}\n\nfunc impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) []neverNilness {\n\tif fn.Object() == nil {\n\t\t// TODO(dh): support closures\n\t\treturn nil\n\t}\n\tif fact := new(neverReturnsNilFact); pass.ImportObjectFact(fn.Object(), fact) {\n\t\treturn fact.Rets\n\t}\n\tif fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {\n\t\treturn nil\n\t}\n\tif fn.Blocks == nil {\n\t\treturn nil\n\t}\n\tif _, ok := seenFns[fn]; ok {\n\t\t// break recursion\n\t\treturn nil\n\t}\n\n\tseenFns[fn] = struct{}{}\n\n\tseen := map[ir.Value]struct{}{}\n\n\tvar mightReturnNil func(v ir.Value) neverNilness\n\tmightReturnNil = func(v ir.Value) neverNilness {\n\t\tif _, ok := seen[v]; ok {\n\t\t\t// break cycle\n\t\t\treturn nilly\n\t\t}\n\t\tif !typeutil.IsPointerLike(v.Type()) {\n\t\t\treturn neverNil\n\t\t}\n\t\tseen[v] = struct{}{}\n\t\tswitch v := v.(type) {\n\t\tcase *ir.MakeInterface:\n\t\t\treturn mightReturnNil(v.X)\n\t\tcase *ir.Convert:\n\t\t\treturn mightReturnNil(v.X)\n\t\tcase *ir.SliceToArrayPointer:\n\t\t\tif typeutil.CoreType(v.Type()).(*types.Pointer).Elem().Underlying().(*types.Array).Len() == 0 {\n\t\t\t\treturn mightReturnNil(v.X)\n\t\t\t} else {\n\t\t\t\t// converting a slice to an array pointer of length > 0 panics if the slice is nil\n\t\t\t\treturn neverNil\n\t\t\t}\n\t\tcase *ir.Slice:\n\t\t\treturn mightReturnNil(v.X)\n\t\tcase *ir.Phi:\n\t\t\tret := neverNil\n\t\t\tfor _, e := range v.Edges {\n\t\t\t\tif n := mightReturnNil(e); n > ret {\n\t\t\t\t\tret = n\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret\n\t\tcase *ir.Extract:\n\t\t\tswitch d := v.Tuple.(type) {\n\t\t\tcase *ir.Call:\n\t\t\t\tif callee := d.Call.StaticCallee(); callee != nil {\n\t\t\t\t\tret := impl(pass, callee, seenFns)\n\t\t\t\t\tif len(ret) == 0 {\n\t\t\t\t\t\treturn nilly\n\t\t\t\t\t}\n\t\t\t\t\treturn ret[v.Index]\n\t\t\t\t} else {\n\t\t\t\t\treturn nilly\n\t\t\t\t}\n\t\t\tcase *ir.TypeAssert, *ir.Next, *ir.Select, *ir.MapLookup, *ir.TypeSwitch, *ir.Recv, *ir.Sigma:\n\t\t\t\t// we don't need to look at the Extract's index\n\t\t\t\t// because we've already checked its type.\n\t\t\t\treturn nilly\n\t\t\tdefault:\n\t\t\t\tpanic(fmt.Sprintf(\"internal error: unhandled type %T\", d))\n\t\t\t}\n\t\tcase *ir.Call:\n\t\t\tif callee := v.Call.StaticCallee(); callee != nil {\n\t\t\t\tret := impl(pass, callee, seenFns)\n\t\t\t\tif len(ret) == 0 {\n\t\t\t\t\treturn nilly\n\t\t\t\t}\n\t\t\t\treturn ret[0]\n\t\t\t} else {\n\t\t\t\treturn nilly\n\t\t\t}\n\t\tcase *ir.BinOp, *ir.UnOp, *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr, *ir.Global, *ir.MakeSlice, *ir.MakeClosure, *ir.Function, *ir.MakeMap, *ir.MakeChan:\n\t\t\treturn neverNil\n\t\tcase *ir.Sigma:\n\t\t\tiff, ok := v.From.Control().(*ir.If)\n\t\t\tif !ok {\n\t\t\t\treturn nilly\n\t\t\t}\n\t\t\tbinop, ok := iff.Cond.(*ir.BinOp)\n\t\t\tif !ok {\n\t\t\t\treturn nilly\n\t\t\t}\n\t\t\tisNil := func(v ir.Value) bool {\n\t\t\t\tk, ok := v.(*ir.Const)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\treturn k.Value == nil\n\t\t\t}\n\t\t\tif binop.X == v.X && isNil(binop.Y) || binop.Y == v.X && isNil(binop.X) {\n\t\t\t\top := binop.Op\n\t\t\t\tif v.From.Succs[0] != v.Block() {\n\t\t\t\t\t// we're in the false branch, negate op\n\t\t\t\t\tswitch op {\n\t\t\t\t\tcase token.EQL:\n\t\t\t\t\t\top = token.NEQ\n\t\t\t\t\tcase token.NEQ:\n\t\t\t\t\t\top = token.EQL\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tpanic(fmt.Sprintf(\"internal error: unhandled token %v\", op))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tswitch op {\n\t\t\t\tcase token.EQL:\n\t\t\t\t\treturn nilly\n\t\t\t\tcase token.NEQ:\n\t\t\t\t\treturn neverNil\n\t\t\t\tdefault:\n\t\t\t\t\tpanic(fmt.Sprintf(\"internal error: unhandled token %v\", op))\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nilly\n\t\tcase *ir.ChangeType:\n\t\t\treturn mightReturnNil(v.X)\n\t\tcase *ir.MultiConvert:\n\t\t\treturn mightReturnNil(v.X)\n\t\tcase *ir.Load:\n\t\t\tif _, ok := v.X.(*ir.Global); ok {\n\t\t\t\treturn onlyGlobal\n\t\t\t}\n\t\t\treturn nilly\n\t\tcase *ir.AggregateConst:\n\t\t\treturn neverNil\n\t\tcase *ir.TypeAssert, *ir.ChangeInterface, *ir.Field, *ir.Const, *ir.GenericConst, *ir.Index, *ir.MapLookup, *ir.Parameter, *ir.Recv, *ir.TypeSwitch:\n\t\t\treturn nilly\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"internal error: unhandled type %T\", v))\n\t\t}\n\t}\n\tret := fn.Exit.Control().(*ir.Return)\n\tout := make([]neverNilness, len(ret.Results))\n\texport := false\n\tfor i, v := range ret.Results {\n\t\t// OPT(dh): couldn't we check the result type's pointer-likeness early, and skip\n\t\t// processing the return value altogether?\n\t\tv := mightReturnNil(v)\n\t\tout[i] = v\n\t\tif v != nilly && typeutil.IsPointerLike(fn.Signature.Results().At(i).Type()) {\n\t\t\texport = true\n\t\t}\n\t}\n\tif export {\n\t\tpass.ExportObjectFact(fn.Object(), &neverReturnsNilFact{out})\n\t}\n\treturn out\n}\n"
  },
  {
    "path": "analysis/facts/nilness/nilness_test.go",
    "content": "package nilness\n\nimport (\n\t\"testing\"\n\n\t\"golang.org/x/tools/go/analysis/analysistest\"\n)\n\nfunc TestNilness(t *testing.T) {\n\tanalysistest.Run(t, analysistest.TestData(), Analysis, \"example.com/Nilness\")\n}\n"
  },
  {
    "path": "analysis/facts/nilness/testdata/src/example.com/Nilness/Nilness.go",
    "content": "package pkg\n\nimport \"errors\"\n\ntype T struct{ f *int }\ntype T2 T\n\nfunc fn1() *T {\n\tif true {\n\t\treturn nil\n\t}\n\treturn &T{}\n}\n\nfunc fn2() *T { // want fn2:`never returns nil: \\[never\\]`\n\treturn &T{}\n}\n\nfunc fn3() *T { // want fn3:`never returns nil: \\[never\\]`\n\treturn new(T)\n}\n\nfunc fn4() *T { // want fn4:`never returns nil: \\[never\\]`\n\treturn fn3()\n}\n\nfunc fn5() *T {\n\treturn fn1()\n}\n\nfunc fn6() *T2 { // want fn6:`never returns nil: \\[never\\]`\n\treturn (*T2)(fn4())\n}\n\nfunc fn7() interface{} {\n\treturn nil\n}\n\nfunc fn8() interface{} { // want fn8:`never returns nil: \\[never\\]`\n\treturn 1\n}\n\nfunc fn9() []int { // want fn9:`never returns nil: \\[never\\]`\n\tx := []int{}\n\ty := x[:1]\n\treturn y\n}\n\nfunc fn10(x []int) []int {\n\treturn x[:1]\n}\n\nfunc fn11(x *T) *T {\n\treturn x\n}\n\nfunc fn12(x *T) *int {\n\treturn x.f\n}\n\nfunc fn13() *int { // want fn13:`never returns nil: \\[never\\]`\n\treturn new(int)\n}\n\nfunc fn14() []int { // want fn14:`never returns nil: \\[never\\]`\n\treturn make([]int, 0)\n}\n\nfunc fn15() []int { // want fn15:`never returns nil: \\[never\\]`\n\treturn []int{}\n}\n\nfunc fn16() []int {\n\treturn nil\n}\n\nfunc fn17() error {\n\tif true {\n\t\treturn errors.New(\"\")\n\t}\n\treturn nil\n}\n\nfunc fn18() (err error) { // want fn18:`never returns nil: \\[never\\]`\n\tfor {\n\t\tif err = fn17(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n}\n\nvar x *int\n\nfunc fn19() *int { // want fn19:`never returns nil: \\[global\\]`\n\treturn x\n}\n\nfunc fn20() *int {\n\tif true {\n\t\treturn x\n\t}\n\treturn nil\n}\n\nfunc fn27[T ~struct{ F int }]() T {\n\treturn T{0}\n}\n\nfunc fn28[T [8]int]() T {\n\treturn T{}\n}\n\nfunc fn29[T []int]() T { // want fn29:`never returns nil: \\[never\\]`\n\treturn T{}\n}\n"
  },
  {
    "path": "analysis/facts/nilness/testdata/src/example.com/Nilness/Nilness_go118.go",
    "content": "//go:build go1.18\n\npackage pkg\n\n// Make sure we don't crash upon seeing a MultiConvert instruction.\nfunc generic1[T []byte | string](s T) T {\n\tswitch v := any(s).(type) {\n\tcase string:\n\t\treturn T(v)\n\tcase []byte:\n\t\treturn T(v)\n\tdefault:\n\t\treturn s\n\t}\n}\n\n// Make sure we don't emit a fact for a function whose return type isn't pointer-like.\nfunc generic2[T [4]byte | string](s T) T {\n\tswitch v := any(s).(type) {\n\tcase string:\n\t\treturn T([]byte(v))\n\tcase [4]byte:\n\t\treturn T(v[:])\n\tdefault:\n\t\treturn s\n\t}\n}\n\n// Make sure we detect that the return value cannot be nil. It is either a string, a\n// non-nil slice we got passed, or a non-nil slice we allocate. Note that we don't\n// understand that the switch's non-default branches are exhaustive over the type set and\n// for the fact to be computed, we have to return something non-nil from the unreachable\n// default branch.\nfunc generic3[T []byte | string](s T) T { // want generic3:`never returns nil: \\[never\\]`\n\tswitch v := any(s).(type) {\n\tcase string:\n\t\treturn T(v)\n\tcase []byte:\n\t\tif v == nil {\n\t\t\treturn T([]byte{})\n\t\t} else {\n\t\t\treturn T(v)\n\t\t}\n\tdefault:\n\t\treturn T([]byte{})\n\t}\n}\n"
  },
  {
    "path": "analysis/facts/nilness/testdata/src/example.com/Nilness/Nilness_go17.go",
    "content": "//go:build go1.17\n// +build go1.17\n\npackage pkg\n\nfunc fn21() *[5]int { // want fn21:`never returns nil: \\[never\\]`\n\tvar x []int\n\treturn (*[5]int)(x)\n}\n\nfunc fn22() *[0]int {\n\tvar x []int\n\treturn (*[0]int)(x)\n}\n\nfunc fn23() *[5]int { // want fn23:`never returns nil: \\[never\\]`\n\tvar x []int\n\ttype T [5]int\n\tret := (*T)(x)\n\treturn (*[5]int)(ret)\n}\n\nfunc fn24() *[0]int {\n\tvar x []int\n\ttype T [0]int\n\tret := (*T)(x)\n\treturn (*[0]int)(ret)\n}\n\nfunc fn25() *[5]int { // want fn25:`never returns nil: \\[never\\]`\n\tvar x []int\n\ttype T *[5]int\n\treturn (T)(x)\n}\n\nfunc fn26() *[0]int {\n\tvar x []int\n\ttype T *[0]int\n\treturn (T)(x)\n}\n"
  },
  {
    "path": "analysis/facts/purity/purity.go",
    "content": "package purity\n\n// TODO(dh): we should split this into two facts, one tracking actual purity, and one tracking side-effects. A function\n// that returns a heap allocation isn't pure, but it may be free of side effects.\n\nimport (\n\t\"go/types\"\n\t\"reflect\"\n\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\ntype IsPure struct{}\n\nfunc (*IsPure) AFact()           {}\nfunc (d *IsPure) String() string { return \"is pure\" }\n\ntype Result map[*types.Func]*IsPure\n\nvar Analyzer = &analysis.Analyzer{\n\tName:       \"fact_purity\",\n\tDoc:        \"Mark pure functions\",\n\tRun:        purity,\n\tRequires:   []*analysis.Analyzer{buildir.Analyzer},\n\tFactTypes:  []analysis.Fact{(*IsPure)(nil)},\n\tResultType: reflect.TypeFor[Result](),\n}\n\nvar pureStdlib = map[string]struct{}{\n\t\"errors.New\":                      {},\n\t\"fmt.Errorf\":                      {},\n\t\"fmt.Sprintf\":                     {},\n\t\"fmt.Sprint\":                      {},\n\t\"sort.Reverse\":                    {},\n\t\"strings.Map\":                     {},\n\t\"strings.Repeat\":                  {},\n\t\"strings.Replace\":                 {},\n\t\"strings.Title\":                   {},\n\t\"strings.ToLower\":                 {},\n\t\"strings.ToLowerSpecial\":          {},\n\t\"strings.ToTitle\":                 {},\n\t\"strings.ToTitleSpecial\":          {},\n\t\"strings.ToUpper\":                 {},\n\t\"strings.ToUpperSpecial\":          {},\n\t\"strings.Trim\":                    {},\n\t\"strings.TrimFunc\":                {},\n\t\"strings.TrimLeft\":                {},\n\t\"strings.TrimLeftFunc\":            {},\n\t\"strings.TrimPrefix\":              {},\n\t\"strings.TrimRight\":               {},\n\t\"strings.TrimRightFunc\":           {},\n\t\"strings.TrimSpace\":               {},\n\t\"strings.TrimSuffix\":              {},\n\t\"(*net/http.Request).WithContext\": {},\n\t\"time.Now\":                        {},\n\t\"time.Parse\":                      {},\n\t\"time.ParseInLocation\":            {},\n\t\"time.Unix\":                       {},\n\t\"time.UnixMicro\":                  {},\n\t\"time.UnixMilli\":                  {},\n\t\"(time.Time).Add\":                 {},\n\t\"(time.Time).AddDate\":             {},\n\t\"(time.Time).After\":               {},\n\t\"(time.Time).Before\":              {},\n\t\"(time.Time).Clock\":               {},\n\t\"(time.Time).Compare\":             {},\n\t\"(time.Time).Date\":                {},\n\t\"(time.Time).Day\":                 {},\n\t\"(time.Time).Equal\":               {},\n\t\"(time.Time).Format\":              {},\n\t\"(time.Time).GoString\":            {},\n\t\"(time.Time).GobEncode\":           {},\n\t\"(time.Time).Hour\":                {},\n\t\"(time.Time).ISOWeek\":             {},\n\t\"(time.Time).In\":                  {},\n\t\"(time.Time).IsDST\":               {},\n\t\"(time.Time).IsZero\":              {},\n\t\"(time.Time).Local\":               {},\n\t\"(time.Time).Location\":            {},\n\t\"(time.Time).MarshalBinary\":       {},\n\t\"(time.Time).MarshalJSON\":         {},\n\t\"(time.Time).MarshalText\":         {},\n\t\"(time.Time).Minute\":              {},\n\t\"(time.Time).Month\":               {},\n\t\"(time.Time).Nanosecond\":          {},\n\t\"(time.Time).Round\":               {},\n\t\"(time.Time).Second\":              {},\n\t\"(time.Time).String\":              {},\n\t\"(time.Time).Sub\":                 {},\n\t\"(time.Time).Truncate\":            {},\n\t\"(time.Time).UTC\":                 {},\n\t\"(time.Time).Unix\":                {},\n\t\"(time.Time).UnixMicro\":           {},\n\t\"(time.Time).UnixMilli\":           {},\n\t\"(time.Time).UnixNano\":            {},\n\t\"(time.Time).Weekday\":             {},\n\t\"(time.Time).Year\":                {},\n\t\"(time.Time).YearDay\":             {},\n\t\"(time.Time).Zone\":                {},\n\t\"(time.Time).ZoneBounds\":          {},\n}\n\nfunc purity(pass *analysis.Pass) (any, error) {\n\tseen := map[*ir.Function]struct{}{}\n\tirpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg\n\tvar check func(fn *ir.Function) (ret bool)\n\tcheck = func(fn *ir.Function) (ret bool) {\n\t\tif fn.Object() == nil {\n\t\t\t// TODO(dh): support closures\n\t\t\treturn false\n\t\t}\n\t\tif pass.ImportObjectFact(fn.Object(), new(IsPure)) {\n\t\t\treturn true\n\t\t}\n\t\tif fn.Pkg != irpkg {\n\t\t\t// Function is in another package but wasn't marked as\n\t\t\t// pure, ergo it isn't pure\n\t\t\treturn false\n\t\t}\n\t\t// Break recursion\n\t\tif _, ok := seen[fn]; ok {\n\t\t\treturn false\n\t\t}\n\n\t\tseen[fn] = struct{}{}\n\t\tdefer func() {\n\t\t\tif ret {\n\t\t\t\tpass.ExportObjectFact(fn.Object(), &IsPure{})\n\t\t\t}\n\t\t}()\n\n\t\tif irutil.IsStub(fn) {\n\t\t\treturn false\n\t\t}\n\n\t\tif _, ok := pureStdlib[fn.Object().(*types.Func).FullName()]; ok {\n\t\t\treturn true\n\t\t}\n\n\t\tif fn.Signature.Results().Len() == 0 {\n\t\t\t// A function with no return values is empty or is doing some\n\t\t\t// work we cannot see (for example because of build tags);\n\t\t\t// don't consider it pure.\n\t\t\treturn false\n\t\t}\n\n\t\tvar isBasic func(typ types.Type) bool\n\t\tisBasic = func(typ types.Type) bool {\n\t\t\tswitch u := typ.Underlying().(type) {\n\t\t\tcase *types.Basic:\n\t\t\t\treturn true\n\t\t\tcase *types.Struct:\n\t\t\t\tfor field := range u.Fields() {\n\t\t\t\t\tif !isBasic(field.Type()) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\tfor _, param := range fn.Params {\n\t\t\t// TODO(dh): this may not be strictly correct. pure code can, to an extent, operate on non-basic types.\n\t\t\tif !isBasic(param.Type()) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\t// Don't consider external functions pure.\n\t\tif fn.Blocks == nil {\n\t\t\treturn false\n\t\t}\n\t\tcheckCall := func(common *ir.CallCommon) bool {\n\t\t\tif common.IsInvoke() {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tbuiltin, ok := common.Value.(*ir.Builtin)\n\t\t\tif !ok {\n\t\t\t\tif common.StaticCallee() != fn {\n\t\t\t\t\tif common.StaticCallee() == nil {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif !check(common.StaticCallee()) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tswitch builtin.Name() {\n\t\t\t\tcase \"len\", \"cap\":\n\t\t\t\tdefault:\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\n\t\tvar isStackAddr func(ir.Value) bool\n\t\tisStackAddr = func(v ir.Value) bool {\n\t\t\tswitch v := v.(type) {\n\t\t\tcase *ir.Alloc:\n\t\t\t\treturn !v.Heap\n\t\t\tcase *ir.FieldAddr:\n\t\t\t\treturn isStackAddr(v.X)\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\tfor _, b := range fn.Blocks {\n\t\t\tfor _, ins := range b.Instrs {\n\t\t\t\tswitch ins := ins.(type) {\n\t\t\t\tcase *ir.Call:\n\t\t\t\t\tif !checkCall(ins.Common()) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\tcase *ir.Defer:\n\t\t\t\t\tif !checkCall(&ins.Call) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\tcase *ir.Select:\n\t\t\t\t\treturn false\n\t\t\t\tcase *ir.Send:\n\t\t\t\t\treturn false\n\t\t\t\tcase *ir.Go:\n\t\t\t\t\treturn false\n\t\t\t\tcase *ir.Panic:\n\t\t\t\t\treturn false\n\t\t\t\tcase *ir.Store:\n\t\t\t\t\tif !isStackAddr(ins.Addr) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\tcase *ir.FieldAddr:\n\t\t\t\t\tif !isStackAddr(ins.X) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\tcase *ir.Alloc:\n\t\t\t\t\t// TODO(dh): make use of proper escape analysis\n\t\t\t\t\tif ins.Heap {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\tcase *ir.Load:\n\t\t\t\t\tif !isStackAddr(ins.X) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tcheck(fn)\n\t}\n\n\tout := Result{}\n\tfor _, fact := range pass.AllObjectFacts() {\n\t\tout[fact.Object.(*types.Func)] = fact.Fact.(*IsPure)\n\t}\n\treturn out, nil\n}\n"
  },
  {
    "path": "analysis/facts/purity/purity_test.go",
    "content": "package purity\n\nimport (\n\t\"testing\"\n\n\t\"golang.org/x/tools/go/analysis/analysistest\"\n)\n\nfunc TestPurity(t *testing.T) {\n\tanalysistest.Run(t, analysistest.TestData(), Analyzer, \"example.com/Purity\")\n}\n"
  },
  {
    "path": "analysis/facts/purity/testdata/src/example.com/Purity/CheckPureFunctions.go",
    "content": "package pkg\n\nfunc foo(a, b int) int { return a + b } // want foo:\"is pure\"\nfunc bar(a, b int) int {\n\tprintln(a + b)\n\treturn a + b\n}\n\nfunc empty()            {}\nfunc stubPointer() *int { return nil }\nfunc stubInt() int      { return 0 }\n\nfunc fn3() {\n\tempty()\n\tstubPointer()\n\tstubInt()\n}\n\nfunc ptr1() *int { return new(int) }\nfunc ptr2() *int { var x int; return &x }\nfunc lit() []int { return []int{} }\n\nvar X int\n\nfunc load() int        { _ = X; return 0 }\nfunc assign(x int) int { _ = x; return 0 } // want assign:\"is pure\"\n\ntype pureStruct1 struct {\n\ta int\n\tb string\n\tpureStruct2\n}\n\ntype pureStruct2 struct {\n\tc float64\n}\n\nfunc (arg pureStruct1) get() int { // want get:\"is pure\"\n\treturn arg.a\n}\n"
  },
  {
    "path": "analysis/facts/tokenfile/token.go",
    "content": "package tokenfile\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\t\"reflect\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar Analyzer = &analysis.Analyzer{\n\tName: \"tokenfileanalyzer\",\n\tDoc:  \"creates a mapping of *token.File to *ast.File\",\n\tRun: func(pass *analysis.Pass) (any, error) {\n\t\tm := map[*token.File]*ast.File{}\n\t\tfor _, af := range pass.Files {\n\t\t\ttf := pass.Fset.File(af.Pos())\n\t\t\tm[tf] = af\n\t\t}\n\t\treturn m, nil\n\t},\n\tRunDespiteErrors: true,\n\tResultType:       reflect.TypeFor[map[*token.File]*ast.File](),\n}\n"
  },
  {
    "path": "analysis/facts/typedness/testdata/src/example.com/Typedness/Typedness.go",
    "content": "package pkg\n\nimport (\n\t\"errors\"\n\t\"os/exec\"\n)\n\ntype T struct{ x *int }\n\nfunc notAStub() {}\n\nfunc fn1() *int             { return nil }\nfunc fn2() (int, *int, int) { return 0, nil, 0 }\n\nfunc fn3() (out1 int, out2 error) { notAStub(); return 0, nil }\nfunc fn4() error                  { notAStub(); return nil }\n\nfunc gen2() (out1 interface{}) { // want gen2:`always typed: 00000001`\n\treturn 1\n}\n\nfunc gen3() (out1 interface{}) { // want gen3:`always typed: 00000001`\n\t// flag, always returns a typed value\n\tm := map[int]*int{}\n\treturn m[0]\n}\n\nfunc gen4() (out1 int, out2 interface{}, out3 *int) { // want gen4:`always typed: 00000010`\n\t// flag ret[1], always a typed value\n\tm := map[int]*int{}\n\treturn 0, m[0], nil\n}\n\nfunc gen5() (out1 interface{}) { // want gen5:`always typed: 00000001`\n\t// flag, propagate gen3\n\treturn gen3()\n}\n\nfunc gen6(b bool) interface{} {\n\t// don't flag, sometimes returns untyped nil\n\tif b {\n\t\tm := map[int]*int{}\n\t\treturn m[0]\n\t} else {\n\t\treturn nil\n\t}\n}\n\nfunc gen7() (out1 interface{}) { // want gen7:`always typed: 00000001`\n\t// flag, always returns a typed value\n\treturn fn1()\n}\n\nfunc gen8(x *int) (out1 interface{}) { // want gen8:`always typed: 00000001`\n\t// flag\n\tif x == nil {\n\t\treturn x\n\t}\n\treturn x\n}\n\nfunc gen9() (out1 interface{}) { // want gen9:`always typed: 00000001`\n\t// flag\n\tvar x *int\n\treturn x\n}\n\nfunc gen10() (out1 interface{}) { // want gen10:`always typed: 00000001`\n\t// flag\n\tvar x *int\n\tif x == nil {\n\t\treturn x\n\t}\n\treturn errors.New(\"\")\n}\n\nfunc gen11() interface{} {\n\t// don't flag, we sometimes return untyped nil\n\tif true {\n\t\treturn nil\n\t} else {\n\t\treturn (*int)(nil)\n\t}\n}\n\nfunc gen12(b bool) (out1 interface{}) { // want gen12:`always typed: 00000001`\n\t// flag, all branches return typed nils\n\tvar x interface{}\n\tif b {\n\t\tx = (*int)(nil)\n\t} else {\n\t\tx = (*string)(nil)\n\t}\n\treturn x\n}\n\nfunc gen13() (out1 interface{}) { // want gen13:`always typed: 00000001`\n\t// flag, always returns a typed value\n\t_, x, _ := fn2()\n\treturn x\n}\n\nfunc gen14(ch chan *int) (out1 interface{}) { // want gen14:`always typed: 00000001`\n\t// flag\n\treturn <-ch\n}\n\nfunc gen15() (out1 interface{}) { // want gen15:`always typed: 00000001`\n\t// flag\n\tt := &T{}\n\treturn t.x\n}\n\nvar g *int = new(int)\n\nfunc gen16() (out1 interface{}) { // want gen16:`always typed: 00000001`\n\treturn g\n}\n\nfunc gen17(x interface{}) interface{} {\n\t// don't flag\n\tif x != nil {\n\t\treturn x\n\t}\n\treturn x\n}\n\nfunc gen18() (int, error) {\n\t// don't flag\n\t_, err := fn3()\n\tif err != nil {\n\t\treturn 0, errors.New(\"yo\")\n\t}\n\treturn 0, err\n}\n\nfunc gen19() (out interface{}) {\n\t// don't flag\n\tif true {\n\t\treturn (*int)(nil)\n\t}\n\treturn\n}\n\nfunc gen20() (out interface{}) {\n\t// don't flag\n\tif true {\n\t\treturn (*int)(nil)\n\t}\n\treturn\n}\n\nfunc gen21() error {\n\tif false {\n\t\treturn (*exec.Error)(nil)\n\t}\n\treturn fn4()\n}\n\nfunc gen22() interface{} {\n\t// don't flag, propagate gen6\n\treturn gen6(false)\n}\n\nfunc gen23() interface{} {\n\treturn gen24()\n}\n\nfunc gen24() interface{} {\n\treturn gen23()\n}\n\nfunc gen25(x interface{}) (out1 interface{}) { // want gen25:`always typed: 00000001`\n\treturn x.(interface{})\n}\n\nfunc gen26(x interface{}) interface{} {\n\tv, _ := x.(interface{})\n\treturn v\n}\n\nfunc gen27(x interface{}) (out1 interface{}) {\n\tdefer recover()\n\tout1 = x.(interface{})\n\treturn out1\n}\n\ntype Error struct{}\n\nfunc (*Error) Error() string { return \"\" }\n\nfunc gen28() (out1 interface{}) { // want gen28:`always typed: 00000001`\n\tx := new(Error)\n\tvar y error = x\n\treturn y\n}\n\nfunc gen29() (out1 interface{}) { // want gen29:`always typed: 00000001`\n\tvar x *Error\n\tvar y error = x\n\treturn y\n}\n\nfunc gen30() (out1, out2 interface{}) { // want gen30:`always typed: 00000011`\n\treturn gen29(), gen28()\n}\n\nfunc gen31() (out1 interface{}) { // want gen31:`always typed: 00000001`\n\ta, _ := gen30()\n\treturn a\n}\n\nfunc gen32() (out1 interface{}) { // want gen32:`always typed: 00000001`\n\t_, b := gen30()\n\treturn b\n}\n\nfunc gen33() (out1 interface{}) { // want gen33:`always typed: 00000001`\n\ta, b := gen30()\n\t_ = a\n\treturn b\n}\n\nfunc gen34() (out1, out2 interface{}) { // want gen34:`always typed: 00000010`\n\treturn nil, 1\n}\n\nfunc gen35() (out1 interface{}) {\n\ta, _ := gen34()\n\treturn a\n}\n\nfunc gen36() (out1 interface{}) { // want gen36:`always typed: 00000001`\n\t_, b := gen34()\n\treturn b\n}\n"
  },
  {
    "path": "analysis/facts/typedness/typedness.go",
    "content": "package typedness\n\nimport (\n\t\"fmt\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"reflect\"\n\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/exp/typeparams\"\n\t\"golang.org/x/tools/go/analysis\"\n)\n\n// alwaysTypedFact denotes that a function's return value will never\n// be untyped nil. The analysis errs on the side of false negatives.\ntype alwaysTypedFact struct {\n\tRets uint8\n}\n\nfunc (*alwaysTypedFact) AFact() {}\nfunc (fact *alwaysTypedFact) String() string {\n\treturn fmt.Sprintf(\"always typed: %08b\", fact.Rets)\n}\n\ntype Result struct {\n\tm map[*types.Func]uint8\n}\n\nvar Analysis = &analysis.Analyzer{\n\tName:       \"typedness\",\n\tDoc:        \"Annotates return values that are always typed values\",\n\tRun:        run,\n\tRequires:   []*analysis.Analyzer{buildir.Analyzer},\n\tFactTypes:  []analysis.Fact{(*alwaysTypedFact)(nil)},\n\tResultType: reflect.TypeFor[*Result](),\n}\n\n// MustReturnTyped reports whether the ret's return value of fn must\n// be a typed value, i.e. an interface value containing a concrete\n// type or trivially a concrete type. The value of ret is zero-based.\n//\n// The analysis has false negatives: MustReturnTyped may incorrectly\n// report false, but never incorrectly reports true.\nfunc (r *Result) MustReturnTyped(fn *types.Func, ret int) bool {\n\tif _, ok := fn.Type().(*types.Signature).Results().At(ret).Type().Underlying().(*types.Interface); !ok {\n\t\treturn true\n\t}\n\treturn (r.m[fn] & (1 << ret)) != 0\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tseen := map[*ir.Function]struct{}{}\n\tout := &Result{\n\t\tm: map[*types.Func]uint8{},\n\t}\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\timpl(pass, fn, seen)\n\t}\n\n\tfor _, fact := range pass.AllObjectFacts() {\n\t\tout.m[fact.Object.(*types.Func)] = fact.Fact.(*alwaysTypedFact).Rets\n\t}\n\n\treturn out, nil\n}\n\nfunc impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) (out uint8) {\n\tif fn.Signature.Results().Len() > 8 {\n\t\treturn 0\n\t}\n\tif fn.Object() == nil {\n\t\t// TODO(dh): support closures\n\t\treturn 0\n\t}\n\tif fact := new(alwaysTypedFact); pass.ImportObjectFact(fn.Object(), fact) {\n\t\treturn fact.Rets\n\t}\n\tif fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {\n\t\treturn 0\n\t}\n\tif fn.Blocks == nil {\n\t\treturn 0\n\t}\n\tif irutil.IsStub(fn) {\n\t\treturn 0\n\t}\n\tif _, ok := seenFns[fn]; ok {\n\t\t// break recursion\n\t\treturn 0\n\t}\n\n\tseenFns[fn] = struct{}{}\n\tdefer func() {\n\t\tfor i := 0; i < fn.Signature.Results().Len(); i++ {\n\t\t\tif _, ok := fn.Signature.Results().At(i).Type().Underlying().(*types.Interface); !ok {\n\t\t\t\t// we don't need facts to know that non-interface\n\t\t\t\t// types can't be untyped nil. zeroing out those bits\n\t\t\t\t// may result in all bits being zero, in which case we\n\t\t\t\t// don't have to save any fact.\n\t\t\t\tout &= ^(1 << i)\n\t\t\t}\n\t\t}\n\t\tif out > 0 {\n\t\t\tpass.ExportObjectFact(fn.Object(), &alwaysTypedFact{out})\n\t\t}\n\t}()\n\n\tisUntypedNil := func(v ir.Value) bool {\n\t\tk, ok := v.(*ir.Const)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\t\tif _, ok := k.Type().Underlying().(*types.Interface); !ok {\n\t\t\treturn false\n\t\t}\n\t\treturn k.Value == nil\n\t}\n\n\tvar do func(v ir.Value, seen map[ir.Value]struct{}) bool\n\tdo = func(v ir.Value, seen map[ir.Value]struct{}) bool {\n\t\tif _, ok := seen[v]; ok {\n\t\t\t// break cycle\n\t\t\treturn false\n\t\t}\n\t\tseen[v] = struct{}{}\n\t\tswitch v := v.(type) {\n\t\tcase *ir.Const:\n\t\t\t// can't be a typed nil, because then we'd be returning the\n\t\t\t// result of MakeInterface.\n\t\t\treturn false\n\t\tcase *ir.ChangeInterface:\n\t\t\treturn do(v.X, seen)\n\t\tcase *ir.Extract:\n\t\t\tcall, ok := v.Tuple.(*ir.Call)\n\t\t\tif !ok {\n\t\t\t\t// We only care about extracts of function results. For\n\t\t\t\t// everything else (e.g. channel receives and map\n\t\t\t\t// lookups), we can either not deduce any information, or\n\t\t\t\t// will see a MakeInterface.\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif callee := call.Call.StaticCallee(); callee != nil {\n\t\t\t\treturn impl(pass, callee, seenFns)&(1<<v.Index) != 0\n\t\t\t} else {\n\t\t\t\t// we don't know what function we're calling. no need\n\t\t\t\t// to look at the signature, though. if it weren't an\n\t\t\t\t// interface, we'd be seeing a MakeInterface\n\t\t\t\t// instruction.\n\t\t\t\treturn false\n\t\t\t}\n\t\tcase *ir.Call:\n\t\t\tif callee := v.Call.StaticCallee(); callee != nil {\n\t\t\t\treturn impl(pass, callee, seenFns)&1 != 0\n\t\t\t} else {\n\t\t\t\t// we don't know what function we're calling. no need\n\t\t\t\t// to look at the signature, though. if it weren't an\n\t\t\t\t// interface, we'd be seeing a MakeInterface\n\t\t\t\t// instruction.\n\t\t\t\treturn false\n\t\t\t}\n\t\tcase *ir.Sigma:\n\t\t\tiff, ok := v.From.Control().(*ir.If)\n\t\t\tif !ok {\n\t\t\t\t// give up\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tbinop, ok := iff.Cond.(*ir.BinOp)\n\t\t\tif !ok {\n\t\t\t\t// give up\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif (binop.X == v.X && isUntypedNil(binop.Y)) || (isUntypedNil(binop.X) && binop.Y == v.X) {\n\t\t\t\top := binop.Op\n\t\t\t\tif v.From.Succs[0] != v.Block() {\n\t\t\t\t\t// we're in the false branch, negate op\n\t\t\t\t\tswitch op {\n\t\t\t\t\tcase token.EQL:\n\t\t\t\t\t\top = token.NEQ\n\t\t\t\t\tcase token.NEQ:\n\t\t\t\t\t\top = token.EQL\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tpanic(fmt.Sprintf(\"internal error: unhandled token %v\", op))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tswitch op {\n\t\t\t\tcase token.EQL:\n\t\t\t\t\t// returned value equals untyped nil\n\t\t\t\t\treturn false\n\t\t\t\tcase token.NEQ:\n\t\t\t\t\t// returned value does not equal untyped nil\n\t\t\t\t\treturn true\n\t\t\t\tdefault:\n\t\t\t\t\tpanic(fmt.Sprintf(\"internal error: unhandled token %v\", op))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// TODO(dh): handle comparison with typed nil\n\n\t\t\t// give up\n\t\t\treturn false\n\t\tcase *ir.Phi:\n\t\t\tfor _, pv := range v.Edges {\n\t\t\t\tif !do(pv, seen) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\tcase *ir.MakeInterface:\n\t\t\tterms, err := typeparams.NormalTerms(v.X.Type())\n\t\t\tif len(terms) == 0 || err != nil {\n\t\t\t\t// Type is a type parameter with no type terms (or we couldn't determine the terms). Such a type\n\t\t\t\t// _can_ be nil when put in an interface value.\n\t\t\t\t//\n\t\t\t\t// There is no instruction that can create a guaranteed non-nil instance of a type parameter without\n\t\t\t\t// type constraints, so we return false right away, without checking v.X's typedness.\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\tcase *ir.TypeAssert:\n\t\t\t// type assertions fail for untyped nils. Either we have a\n\t\t\t// single lhs and the type assertion succeeds or panics,\n\t\t\t// or we have two lhs and we'll return Extract instead.\n\t\t\treturn true\n\t\tcase *ir.ChangeType:\n\t\t\t// we'll only see interface->interface conversions, which\n\t\t\t// don't tell us anything about the nilness.\n\t\t\treturn false\n\t\tcase *ir.MapLookup, *ir.Index, *ir.Recv, *ir.Parameter, *ir.Load, *ir.Field:\n\t\t\t// All other instructions that tell us nothing about the\n\t\t\t// typedness of interface values.\n\t\t\treturn false\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"internal error: unhandled type %T\", v))\n\t\t}\n\t}\n\n\tret := fn.Exit.Control().(*ir.Return)\n\tfor i, v := range ret.Results {\n\t\ttyp := fn.Signature.Results().At(i).Type()\n\t\tif _, ok := typ.Underlying().(*types.Interface); ok && !typeparams.IsTypeParam(typ) {\n\t\t\tif do(v, map[ir.Value]struct{}{}) {\n\t\t\t\tout |= 1 << i\n\t\t\t}\n\t\t}\n\t}\n\treturn out\n}\n"
  },
  {
    "path": "analysis/facts/typedness/typedness_test.go",
    "content": "package typedness\n\nimport (\n\t\"testing\"\n\n\t\"golang.org/x/tools/go/analysis/analysistest\"\n)\n\nfunc TestTypedness(t *testing.T) {\n\tanalysistest.Run(t, analysistest.TestData(), Analysis, \"example.com/Typedness\")\n}\n"
  },
  {
    "path": "analysis/lint/lint.go",
    "content": "// Package lint provides abstractions on top of go/analysis.\n// These abstractions add extra information to analyzes, such as structured documentation and severities.\npackage lint\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"strings\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"honnef.co/go/tools/analysis/facts/tokenfile\"\n)\n\n// Analyzer wraps a go/analysis.Analyzer and provides structured documentation.\ntype Analyzer struct {\n\t// The analyzer's documentation. Unlike go/analysis.Analyzer.Doc,\n\t// this field is structured, providing access to severity, options\n\t// etc.\n\tDoc      *RawDocumentation\n\tAnalyzer *analysis.Analyzer\n}\n\nfunc InitializeAnalyzer(a *Analyzer) *Analyzer {\n\ta.Analyzer.Doc = a.Doc.Compile().String()\n\ta.Analyzer.URL = \"https://staticcheck.dev/docs/checks/#\" + a.Analyzer.Name\n\ta.Analyzer.Requires = append(a.Analyzer.Requires, tokenfile.Analyzer)\n\treturn a\n}\n\n// Severity describes the severity of diagnostics reported by an analyzer.\ntype Severity int\n\nconst (\n\tSeverityNone Severity = iota\n\tSeverityError\n\tSeverityDeprecated\n\tSeverityWarning\n\tSeverityInfo\n\tSeverityHint\n)\n\n// MergeStrategy sets how merge mode should behave for diagnostics of an analyzer.\ntype MergeStrategy int\n\nconst (\n\tMergeIfAny MergeStrategy = iota\n\tMergeIfAll\n)\n\ntype RawDocumentation struct {\n\tTitle      string\n\tText       string\n\tBefore     string\n\tAfter      string\n\tSince      string\n\tNonDefault bool\n\tOptions    []string\n\tSeverity   Severity\n\tMergeIf    MergeStrategy\n}\n\ntype Documentation struct {\n\tTitle string\n\tText  string\n\n\tTitleMarkdown string\n\tTextMarkdown  string\n\n\tBefore     string\n\tAfter      string\n\tSince      string\n\tNonDefault bool\n\tOptions    []string\n\tSeverity   Severity\n\tMergeIf    MergeStrategy\n}\n\nfunc (doc RawDocumentation) Compile() *Documentation {\n\treturn &Documentation{\n\t\tTitle: strings.TrimSpace(stripMarkdown(doc.Title)),\n\t\tText:  strings.TrimSpace(stripMarkdown(doc.Text)),\n\n\t\tTitleMarkdown: strings.TrimSpace(toMarkdown(doc.Title)),\n\t\tTextMarkdown:  strings.TrimSpace(toMarkdown(doc.Text)),\n\n\t\tBefore:     strings.TrimSpace(doc.Before),\n\t\tAfter:      strings.TrimSpace(doc.After),\n\t\tSince:      doc.Since,\n\t\tNonDefault: doc.NonDefault,\n\t\tOptions:    doc.Options,\n\t\tSeverity:   doc.Severity,\n\t\tMergeIf:    doc.MergeIf,\n\t}\n}\n\nfunc toMarkdown(s string) string {\n\treturn strings.NewReplacer(`\\'`, \"`\", `\\\"`, \"`\").Replace(s)\n}\n\nfunc stripMarkdown(s string) string {\n\treturn strings.NewReplacer(`\\'`, \"\", `\\\"`, \"'\").Replace(s)\n}\n\nfunc (doc *Documentation) Format(metadata bool) string {\n\treturn doc.format(false, metadata)\n}\n\nfunc (doc *Documentation) FormatMarkdown(metadata bool) string {\n\treturn doc.format(true, metadata)\n}\n\nfunc (doc *Documentation) format(markdown bool, metadata bool) string {\n\tb := &strings.Builder{}\n\tif markdown {\n\t\tfmt.Fprintf(b, \"%s\\n\\n\", doc.TitleMarkdown)\n\t\tif doc.Text != \"\" {\n\t\t\tfmt.Fprintf(b, \"%s\\n\\n\", doc.TextMarkdown)\n\t\t}\n\t} else {\n\t\tfmt.Fprintf(b, \"%s\\n\\n\", doc.Title)\n\t\tif doc.Text != \"\" {\n\t\t\tfmt.Fprintf(b, \"%s\\n\\n\", doc.Text)\n\t\t}\n\t}\n\n\tif doc.Before != \"\" {\n\t\tfmt.Fprintln(b, \"Before:\")\n\t\tfmt.Fprintln(b, \"\")\n\t\tfor line := range strings.SplitSeq(doc.Before, \"\\n\") {\n\t\t\tfmt.Fprint(b, \"    \", line, \"\\n\")\n\t\t}\n\t\tfmt.Fprintln(b, \"\")\n\t\tfmt.Fprintln(b, \"After:\")\n\t\tfmt.Fprintln(b, \"\")\n\t\tfor line := range strings.SplitSeq(doc.After, \"\\n\") {\n\t\t\tfmt.Fprint(b, \"    \", line, \"\\n\")\n\t\t}\n\t\tfmt.Fprintln(b, \"\")\n\t}\n\n\tif metadata {\n\t\tfmt.Fprint(b, \"Available since\\n    \")\n\t\tif doc.Since == \"\" {\n\t\t\tfmt.Fprint(b, \"unreleased\")\n\t\t} else {\n\t\t\tfmt.Fprintf(b, \"%s\", doc.Since)\n\t\t}\n\t\tif doc.NonDefault {\n\t\t\tfmt.Fprint(b, \", non-default\")\n\t\t}\n\t\tfmt.Fprint(b, \"\\n\")\n\t\tif len(doc.Options) > 0 {\n\t\t\tfmt.Fprintf(b, \"\\nOptions\\n\")\n\t\t\tfor _, opt := range doc.Options {\n\t\t\t\tfmt.Fprintf(b, \"    %s\", opt)\n\t\t\t}\n\t\t\tfmt.Fprint(b, \"\\n\")\n\t\t}\n\t}\n\n\treturn b.String()\n}\n\nfunc (doc *Documentation) String() string {\n\treturn doc.Format(true)\n}\n\n// ExhaustiveTypeSwitch panics when called. It can be used to ensure\n// that type switches are exhaustive.\nfunc ExhaustiveTypeSwitch(v any) {\n\tpanic(fmt.Sprintf(\"internal error: unhandled case %T\", v))\n}\n\n// A directive is a comment of the form '//lint:<command>\n// [arguments...]'. It represents instructions to the static analysis\n// tool.\ntype Directive struct {\n\tCommand   string\n\tArguments []string\n\tDirective *ast.Comment\n\tNode      ast.Node\n}\n\nfunc parseDirective(s string) (cmd string, args []string) {\n\tif !strings.HasPrefix(s, \"//lint:\") {\n\t\treturn \"\", nil\n\t}\n\ts = strings.TrimPrefix(s, \"//lint:\")\n\tfields := strings.Split(s, \" \")\n\treturn fields[0], fields[1:]\n}\n\n// ParseDirectives extracts all directives from a list of Go files.\nfunc ParseDirectives(files []*ast.File, fset *token.FileSet) []Directive {\n\tvar dirs []Directive\n\tfor _, f := range files {\n\t\t// OPT(dh): in our old code, we skip all the comment map work if we\n\t\t// couldn't find any directives, benchmark if that's actually\n\t\t// worth doing\n\t\tcm := ast.NewCommentMap(fset, f, f.Comments)\n\t\tfor node, cgs := range cm {\n\t\t\tfor _, cg := range cgs {\n\t\t\t\tfor _, c := range cg.List {\n\t\t\t\t\tif !strings.HasPrefix(c.Text, \"//lint:\") {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tcmd, args := parseDirective(c.Text)\n\t\t\t\t\td := Directive{\n\t\t\t\t\t\tCommand:   cmd,\n\t\t\t\t\t\tArguments: args,\n\t\t\t\t\t\tDirective: c,\n\t\t\t\t\t\tNode:      node,\n\t\t\t\t\t}\n\t\t\t\t\tdirs = append(dirs, d)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn dirs\n}\n"
  },
  {
    "path": "analysis/lint/testutil/check.go",
    "content": "// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// This file is a modified copy of x/tools/go/analysis/analysistest/analysistest.go\n\npackage testutil\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/format\"\n\t\"go/token\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"honnef.co/go/tools/internal/diff/myers\"\n\t\"honnef.co/go/tools/lintcmd/runner\"\n\n\t\"golang.org/x/tools/go/expect\"\n\t\"golang.org/x/tools/txtar\"\n)\n\nfunc CheckSuggestedFixes(t *testing.T, diagnostics []runner.Diagnostic) {\n\t// Process each result (package) separately, matching up the suggested\n\t// fixes into a diff, which we will compare to the .golden file.  We have\n\t// to do this per-result in case a file appears in two packages, such as in\n\t// packages with tests, where mypkg/a.go will appear in both mypkg and\n\t// mypkg.test.  In that case, the analyzer may suggest the same set of\n\t// changes to a.go for each package.  If we merge all the results, those\n\t// changes get doubly applied, which will cause conflicts or mismatches.\n\t// Validating the results separately means as long as the two analyses\n\t// don't produce conflicting suggestions for a single file, everything\n\t// should match up.\n\t// file -> message -> edits\n\tfileEdits := make(map[string]map[string][]runner.TextEdit)\n\tfileContents := make(map[string][]byte)\n\n\t// Validate edits, prepare the fileEdits map and read the file contents.\n\tfor _, diag := range diagnostics {\n\t\tfor _, sf := range diag.SuggestedFixes {\n\t\t\tfor _, edit := range sf.TextEdits {\n\t\t\t\t// Validate the edit.\n\t\t\t\tif edit.Position.Offset > edit.End.Offset {\n\t\t\t\t\tt.Errorf(\n\t\t\t\t\t\t\"diagnostic for analysis %v contains Suggested Fix with malformed edit: pos (%v) > end (%v)\",\n\t\t\t\t\t\tdiag.Category, edit.Position.Offset, edit.End.Offset)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif edit.Position.Filename != edit.End.Filename {\n\t\t\t\t\tt.Errorf(\n\t\t\t\t\t\t\"diagnostic for analysis %v contains Suggested Fix with malformed edit spanning files %v and %v\",\n\t\t\t\t\t\tdiag.Category, edit.Position.Filename, edit.End.Filename)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif _, ok := fileContents[edit.Position.Filename]; !ok {\n\t\t\t\t\tcontents, err := os.ReadFile(edit.Position.Filename)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Errorf(\"error reading %s: %v\", edit.Position.Filename, err)\n\t\t\t\t\t}\n\t\t\t\t\tfileContents[edit.Position.Filename] = contents\n\t\t\t\t}\n\n\t\t\t\tif _, ok := fileEdits[edit.Position.Filename]; !ok {\n\t\t\t\t\tfileEdits[edit.Position.Filename] = make(map[string][]runner.TextEdit)\n\t\t\t\t}\n\t\t\t\tfileEdits[edit.Position.Filename][sf.Message] = append(fileEdits[edit.Position.Filename][sf.Message], edit)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor file, fixes := range fileEdits {\n\t\t// Get the original file contents.\n\t\torig, ok := fileContents[file]\n\t\tif !ok {\n\t\t\tt.Errorf(\"could not find file contents for %s\", file)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Get the golden file and read the contents.\n\t\tar, err := txtar.ParseFile(file + \".golden\")\n\t\tif err != nil {\n\t\t\tt.Errorf(\"error reading %s.golden: %v\", file, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(ar.Files) > 0 {\n\t\t\t// one virtual file per kind of suggested fix\n\n\t\t\tif len(ar.Comment) != 0 {\n\t\t\t\t// we allow either just the comment, or just virtual\n\t\t\t\t// files, not both. it is not clear how \"both\" should\n\t\t\t\t// behave.\n\t\t\t\tt.Errorf(\"%s.golden has leading comment; we don't know what to do with it\", file)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tvar sfs []string\n\t\t\tfor sf := range fixes {\n\t\t\t\tsfs = append(sfs, sf)\n\t\t\t}\n\t\t\tslices.Sort(sfs)\n\t\t\tfor _, sf := range sfs {\n\t\t\t\tedits := fixes[sf]\n\t\t\t\tfound := false\n\t\t\t\tfor _, vf := range ar.Files {\n\t\t\t\t\tif vf.Name == sf {\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t\tout := applyEdits(orig, edits)\n\t\t\t\t\t\t// the file may contain multiple trailing\n\t\t\t\t\t\t// newlines if the user places empty lines\n\t\t\t\t\t\t// between files in the archive. normalize\n\t\t\t\t\t\t// this to a single newline.\n\t\t\t\t\t\twant := string(bytes.TrimRight(vf.Data, \"\\n\")) + \"\\n\"\n\t\t\t\t\t\tformatted, err := format.Source([]byte(out))\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tt.Errorf(\"%s: error formatting edited source: %v\\n%s\", file, err, out)\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif want != string(formatted) {\n\t\t\t\t\t\t\td := myers.ComputeEdits(want, string(formatted))\n\t\t\t\t\t\t\tvar diff strings.Builder\n\t\t\t\t\t\t\tfor _, op := range d {\n\t\t\t\t\t\t\t\tdiff.WriteString(op.String())\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tt.Errorf(\"suggested fixes failed for %s[%s]:\\n%s\", file, sf, diff.String())\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !found {\n\t\t\t\t\tt.Errorf(\"no section for suggested fix %q in %s.golden\", sf, file)\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor _, vf := range ar.Files {\n\t\t\t\tif _, ok := fixes[vf.Name]; !ok {\n\t\t\t\t\tt.Errorf(\"%s.golden has section for suggested fix %q, but we didn't produce any fix by that name\", file, vf.Name)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// all suggested fixes are represented by a single file\n\n\t\t\tvar catchallEdits []runner.TextEdit\n\t\t\tfor _, edits := range fixes {\n\t\t\t\tcatchallEdits = append(catchallEdits, edits...)\n\t\t\t}\n\n\t\t\tout := applyEdits(orig, catchallEdits)\n\t\t\twant := string(ar.Comment)\n\n\t\t\tformatted, err := format.Source([]byte(out))\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"%s: error formatting resulting source: %v\\n%s\", file, err, out)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif want != string(formatted) {\n\t\t\t\td := myers.ComputeEdits(want, string(formatted))\n\t\t\t\tvar diff strings.Builder\n\t\t\t\tfor _, op := range d {\n\t\t\t\t\tdiff.WriteString(op.String())\n\t\t\t\t}\n\t\t\t\tt.Errorf(\"suggested fixes failed for %s:\\n%s\", file, diff.String())\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc Check(t *testing.T, gopath string, files []string, diagnostics []runner.Diagnostic, facts []runner.TestFact) {\n\trelativePath := func(path string) string {\n\t\tcwd, err := os.Getwd()\n\t\tif err != nil {\n\t\t\treturn path\n\t\t}\n\t\trel, err := filepath.Rel(cwd, path)\n\t\tif err != nil {\n\t\t\treturn path\n\t\t}\n\t\treturn rel\n\t}\n\n\ttype key struct {\n\t\tfile string\n\t\tline int\n\t}\n\n\t// the 'files' argument contains a list of all files that were part of the tested package\n\twant := make(map[key][]*expect.Note)\n\n\tfset := token.NewFileSet()\n\tseen := map[string]struct{}{}\n\tfor _, file := range files {\n\t\tseen[file] = struct{}{}\n\n\t\tnotes, err := expect.Parse(fset, file, nil)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tfor _, note := range notes {\n\t\t\tk := key{\n\t\t\t\tfile: file,\n\t\t\t\tline: fset.PositionFor(note.Pos, false).Line,\n\t\t\t}\n\t\t\twant[k] = append(want[k], note)\n\t\t}\n\t}\n\n\tfor _, diag := range diagnostics {\n\t\tfile := diag.Position.Filename\n\t\tif _, ok := seen[file]; !ok {\n\t\t\tt.Errorf(\"got diagnostic in file %q, but that file isn't part of the checked package\", relativePath(file))\n\t\t\treturn\n\t\t}\n\t}\n\n\tcheck := func(posn token.Position, message string, kind string, argIdx int, identifier string) {\n\t\tk := key{posn.Filename, posn.Line}\n\t\texpects := want[k]\n\t\tvar unmatched []string\n\t\tfor i, exp := range expects {\n\t\t\tif exp.Name == kind {\n\t\t\t\tif kind == \"fact\" && exp.Args[0] != expect.Identifier(identifier) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tmatched := false\n\t\t\t\tswitch arg := exp.Args[argIdx].(type) {\n\t\t\t\tcase string:\n\t\t\t\t\tmatched = strings.Contains(message, arg)\n\t\t\t\tcase *regexp.Regexp:\n\t\t\t\t\tmatched = arg.MatchString(message)\n\t\t\t\tdefault:\n\t\t\t\t\tt.Fatalf(\"unexpected argument type %T\", arg)\n\t\t\t\t}\n\t\t\t\tif matched {\n\t\t\t\t\t// matched: remove the expectation.\n\t\t\t\t\texpects[i] = expects[len(expects)-1]\n\t\t\t\t\texpects = expects[:len(expects)-1]\n\t\t\t\t\twant[k] = expects\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tunmatched = append(unmatched, fmt.Sprintf(\"%q\", exp.Args[argIdx]))\n\t\t\t}\n\t\t}\n\t\tif unmatched == nil {\n\t\t\tposn.Filename = relativePath(posn.Filename)\n\t\t\tt.Errorf(\"%v: unexpected diag: %v\", posn, message)\n\t\t} else {\n\t\t\tposn.Filename = relativePath(posn.Filename)\n\t\t\tt.Errorf(\"%v: diag %q does not match pattern %s\",\n\t\t\t\tposn, message, strings.Join(unmatched, \" or \"))\n\t\t}\n\t}\n\n\tcheckDiag := func(posn token.Position, message string) {\n\t\tcheck(posn, message, \"diag\", 0, \"\")\n\t}\n\n\tcheckFact := func(posn token.Position, name, message string) {\n\t\tcheck(posn, message, \"fact\", 1, name)\n\t}\n\n\t// Check the diagnostics match expectations.\n\tfor _, f := range diagnostics {\n\t\t// TODO(matloob): Support ranges in analysistest.\n\t\tposn := f.Position\n\t\tcheckDiag(posn, f.Message)\n\t}\n\n\t// Check the facts match expectations.\n\tfor _, fact := range facts {\n\t\tname := fact.ObjectName\n\t\tposn := fact.Position\n\t\tif name == \"\" {\n\t\t\tname = \"package\"\n\t\t\tposn.Line = 1\n\t\t}\n\n\t\tcheckFact(posn, name, fact.FactString)\n\t}\n\n\t// Reject surplus expectations.\n\t//\n\t// Sometimes an Analyzer reports two similar diagnostics on a\n\t// line with only one expectation. The reader may be confused by\n\t// the error message.\n\t// TODO(adonovan): print a better error:\n\t// \"got 2 diagnostics here; each one needs its own expectation\".\n\tvar surplus []string\n\tfor key, expects := range want {\n\t\tfor _, exp := range expects {\n\t\t\tsurplus = append(surplus, fmt.Sprintf(\"%s:%d: no %s was reported matching %q\", relativePath(key.file), key.line, exp.Name, exp.Args))\n\t\t}\n\t}\n\tsort.Strings(surplus)\n\tfor _, err := range surplus {\n\t\tt.Errorf(\"%s\", err)\n\t}\n}\n\nfunc applyEdits(src []byte, edits []runner.TextEdit) []byte {\n\t// This function isn't efficient, but it doesn't have to be.\n\n\tedits = slices.Clone(edits)\n\tsort.Slice(edits, func(i, j int) bool {\n\t\tif edits[i].Position.Offset < edits[j].Position.Offset {\n\t\t\treturn true\n\t\t}\n\t\tif edits[i].Position.Offset == edits[j].Position.Offset {\n\t\t\treturn edits[i].End.Offset < edits[j].End.Offset\n\t\t}\n\t\treturn false\n\t})\n\n\tout := bytes.Clone(src)\n\toffset := 0\n\tfor _, edit := range edits {\n\t\tstart := edit.Position.Offset + offset\n\t\tend := edit.End.Offset + offset\n\t\tif edit.End == (token.Position{}) {\n\t\t\tend = -1\n\t\t}\n\t\tif len(edit.NewText) == 0 {\n\t\t\t// pure deletion\n\t\t\tcopy(out[start:], out[end:])\n\t\t\tout = out[:len(out)-(end-start)]\n\t\t\toffset -= end - start\n\t\t} else if end == -1 || end == start {\n\t\t\t// pure insertion\n\t\t\ttmp := make([]byte, len(out)+len(edit.NewText))\n\t\t\tcopy(tmp, out[:start])\n\t\t\tcopy(tmp[start:], edit.NewText)\n\t\t\tcopy(tmp[start+len(edit.NewText):], out[start:])\n\t\t\toffset += len(edit.NewText)\n\t\t\tout = tmp\n\t\t} else if end-start == len(edit.NewText) {\n\t\t\t// exact replacement\n\t\t\tcopy(out[start:], edit.NewText)\n\t\t} else if end-start < len(edit.NewText) {\n\t\t\t// replace with longer string\n\t\t\tgrowth := len(edit.NewText) - (end - start)\n\t\t\ttmp := make([]byte, len(out)+growth)\n\t\t\tcopy(tmp, out[:start])\n\t\t\tcopy(tmp[start:], edit.NewText)\n\t\t\tcopy(tmp[start+len(edit.NewText):], out[end:])\n\t\t\toffset += growth\n\t\t\tout = tmp\n\t\t} else if end-start > len(edit.NewText) {\n\t\t\t// replace with shorter string\n\t\t\tshrinkage := (end - start) - len(edit.NewText)\n\n\t\t\tcopy(out[start:], edit.NewText)\n\t\t\tcopy(out[start+len(edit.NewText):], out[end:])\n\t\t\tout = out[:len(out)-shrinkage]\n\t\t\toffset -= shrinkage\n\t\t}\n\t}\n\n\t// Debug code\n\tif false {\n\t\tfmt.Println(\"input:\")\n\t\tfmt.Println(string(src))\n\t\tfmt.Println()\n\t\tfmt.Println(\"edits:\")\n\t\tfor _, edit := range edits {\n\t\t\tfmt.Printf(\"%d:%d - %d:%d <- %q\\n\", edit.Position.Line, edit.Position.Column, edit.End.Line, edit.End.Column, edit.NewText)\n\t\t}\n\t\tfmt.Println(\"output:\")\n\t\tfmt.Println(string(out))\n\t\tpanic(\"\")\n\t}\n\n\treturn out\n}\n"
  },
  {
    "path": "analysis/lint/testutil/util.go",
    "content": "package testutil\n\nimport (\n\t\"crypto/sha256\"\n\t\"go/build\"\n\t\"go/version\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/config\"\n\t\"honnef.co/go/tools/go/buildid\"\n\t\"honnef.co/go/tools/lintcmd/cache\"\n\t\"honnef.co/go/tools/lintcmd/runner\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/packages\"\n)\n\ntype Test struct {\n\tDir     string\n\tVersion string\n}\n\nfunc computeSalt() ([]byte, error) {\n\tp, err := os.Executable()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif id, err := buildid.ReadFile(p); err == nil {\n\t\treturn []byte(id), nil\n\t} else {\n\t\t// For some reason we couldn't read the build id from the executable.\n\t\t// Fall back to hashing the entire executable.\n\t\tf, err := os.Open(p)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer f.Close()\n\t\th := sha256.New()\n\t\tif _, err := io.Copy(h, f); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn h.Sum(nil), nil\n\t}\n}\n\nfunc Run(t *testing.T, a *lint.Analyzer) {\n\tdirs, err := filepath.Glob(\"testdata/*\")\n\tif err != nil {\n\t\tt.Fatalf(\"couldn't enumerate test data: %s\", err)\n\t}\n\n\tif len(dirs) == 0 {\n\t\tt.Fatalf(\"found no tests\")\n\t}\n\n\tc, err := cache.Open(t.TempDir())\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tsalt, err := computeSalt()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tc.SetSalt(salt)\n\n\ttags := build.Default.ReleaseTags\n\tmaxVersion := tags[len(tags)-1]\n\tfor _, dir := range dirs {\n\t\tvers := filepath.Base(dir)\n\t\tt.Run(vers, func(t *testing.T) {\n\t\t\tif !version.IsValid(vers) {\n\t\t\t\tt.Fatalf(\"%q is not a valid Go version\", dir)\n\t\t\t}\n\t\t\tif version.Compare(vers, maxVersion) == 1 {\n\t\t\t\tt.Skipf(\"%s is newer than our Go version (%s), skipping\", vers, maxVersion)\n\t\t\t}\n\t\t\tr, err := runner.New(config.Config{}, c)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tr.TestMode = true\n\n\t\t\ttestdata, err := filepath.Abs(\"testdata\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tcfg := &packages.Config{\n\t\t\t\tDir:   dir,\n\t\t\t\tTests: true,\n\t\t\t\tEnv:   append(os.Environ(), \"GOPROXY=off\", \"GOFLAGS=-mod=vendor\", \"GO111MODULE=\"),\n\t\t\t\tOverlay: map[string][]byte{\n\t\t\t\t\t\"go.mod\": []byte(\"module example.com\\ngo \" + strings.TrimPrefix(vers, \"go\")),\n\t\t\t\t},\n\t\t\t}\n\t\t\tres, err := r.Run(cfg, []*analysis.Analyzer{a.Analyzer}, []string{\"./...\"})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tif len(res) == 0 {\n\t\t\t\tt.Fatalf(\"got no results for %s/...\", dir)\n\t\t\t}\n\n\t\t\tfor _, r := range res {\n\t\t\t\tif r.Failed {\n\t\t\t\t\tif len(r.Errors) > 0 {\n\t\t\t\t\t\tsb := strings.Builder{}\n\t\t\t\t\t\tfor _, err := range r.Errors {\n\t\t\t\t\t\t\tsb.WriteString(err.Error())\n\t\t\t\t\t\t\tsb.WriteString(\"\\n\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tt.Fatalf(\"failed checking %s:\\n%s\", r.Package.PkgPath, sb.String())\n\t\t\t\t\t} else {\n\t\t\t\t\t\tt.Fatalf(\"failed processing package %s, but got no errors\", r.Package.PkgPath)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdata, err := r.Load()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t\ttdata, err := r.LoadTest()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\n\t\t\t\trelevantDiags := data.Diagnostics\n\t\t\t\tvar relevantFacts []runner.TestFact\n\t\t\t\tfor _, fact := range tdata.Facts {\n\t\t\t\t\tif fact.Analyzer != a.Analyzer.Name {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\trelevantFacts = append(relevantFacts, fact)\n\t\t\t\t}\n\n\t\t\t\tCheck(t, testdata, tdata.Files, relevantDiags, relevantFacts)\n\t\t\t\tCheckSuggestedFixes(t, relevantDiags)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "analysis/report/report.go",
    "content": "package report\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/format\"\n\t\"go/token\"\n\t\"go/version\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\ntype Options struct {\n\tShortRange             bool\n\tFilterGenerated        bool\n\tFixes                  []analysis.SuggestedFix\n\tRelated                []analysis.RelatedInformation\n\tMinimumLanguageVersion string\n\tMaximumLanguageVersion string\n\tMinimumStdlibVersion   string\n\tMaximumStdlibVersion   string\n}\n\ntype Option func(*Options)\n\nfunc ShortRange() Option {\n\treturn func(opts *Options) {\n\t\topts.ShortRange = true\n\t}\n}\n\nfunc FilterGenerated() Option {\n\treturn func(opts *Options) {\n\t\topts.FilterGenerated = true\n\t}\n}\n\nfunc Fixes(fixes ...analysis.SuggestedFix) Option {\n\treturn func(opts *Options) {\n\t\topts.Fixes = append(opts.Fixes, fixes...)\n\t}\n}\n\nfunc Related(node Positioner, message string) Option {\n\treturn func(opts *Options) {\n\t\tpos, end, ok := getRange(node, opts.ShortRange)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tr := analysis.RelatedInformation{\n\t\t\tPos:     pos,\n\t\t\tEnd:     end,\n\t\t\tMessage: message,\n\t\t}\n\t\topts.Related = append(opts.Related, r)\n\t}\n}\n\nfunc MinimumLanguageVersion(vers string) Option {\n\treturn func(opts *Options) { opts.MinimumLanguageVersion = vers }\n}\nfunc MaximumLanguageVersion(vers string) Option {\n\treturn func(opts *Options) { opts.MinimumLanguageVersion = vers }\n}\nfunc MinimumStdlibVersion(vers string) Option {\n\treturn func(opts *Options) { opts.MinimumStdlibVersion = vers }\n}\nfunc MaximumStdlibVersion(vers string) Option {\n\treturn func(opts *Options) { opts.MaximumStdlibVersion = vers }\n}\n\ntype Positioner interface {\n\tPos() token.Pos\n}\n\ntype fullPositioner interface {\n\tPos() token.Pos\n\tEnd() token.Pos\n}\n\ntype sourcer interface {\n\tSource() ast.Node\n}\n\n// shortRange returns the position and end of the main component of an\n// AST node. For nodes that have no body, the short range is identical\n// to the node's Pos and End. For nodes that do have a body, the short\n// range excludes the body.\nfunc shortRange(node ast.Node) (pos, end token.Pos) {\n\tswitch node := node.(type) {\n\tcase *ast.File:\n\t\treturn node.Pos(), node.Name.End()\n\tcase *ast.CaseClause:\n\t\treturn node.Pos(), node.Colon + 1\n\tcase *ast.CommClause:\n\t\treturn node.Pos(), node.Colon + 1\n\tcase *ast.DeferStmt:\n\t\treturn node.Pos(), node.Defer + token.Pos(len(\"defer\"))\n\tcase *ast.ExprStmt:\n\t\treturn shortRange(node.X)\n\tcase *ast.ForStmt:\n\t\tif node.Post != nil {\n\t\t\treturn node.For, node.Post.End()\n\t\t} else if node.Cond != nil {\n\t\t\treturn node.For, node.Cond.End()\n\t\t} else if node.Init != nil {\n\t\t\t// +1 to catch the semicolon, for gofmt'ed code\n\t\t\treturn node.Pos(), node.Init.End() + 1\n\t\t} else {\n\t\t\treturn node.Pos(), node.For + token.Pos(len(\"for\"))\n\t\t}\n\tcase *ast.FuncDecl:\n\t\treturn node.Pos(), node.Type.End()\n\tcase *ast.FuncLit:\n\t\treturn node.Pos(), node.Type.End()\n\tcase *ast.GoStmt:\n\t\tif _, ok := astutil.Unparen(node.Call.Fun).(*ast.FuncLit); ok {\n\t\t\treturn node.Pos(), node.Go + token.Pos(len(\"go\"))\n\t\t} else {\n\t\t\treturn node.Pos(), node.End()\n\t\t}\n\tcase *ast.IfStmt:\n\t\treturn node.Pos(), node.Cond.End()\n\tcase *ast.RangeStmt:\n\t\treturn node.Pos(), node.X.End()\n\tcase *ast.SelectStmt:\n\t\treturn node.Pos(), node.Pos() + token.Pos(len(\"select\"))\n\tcase *ast.SwitchStmt:\n\t\tif node.Tag != nil {\n\t\t\treturn node.Pos(), node.Tag.End()\n\t\t} else if node.Init != nil {\n\t\t\t// +1 to catch the semicolon, for gofmt'ed code\n\t\t\treturn node.Pos(), node.Init.End() + 1\n\t\t} else {\n\t\t\treturn node.Pos(), node.Pos() + token.Pos(len(\"switch\"))\n\t\t}\n\tcase *ast.TypeSwitchStmt:\n\t\treturn node.Pos(), node.Assign.End()\n\tdefault:\n\t\treturn node.Pos(), node.End()\n\t}\n}\n\nfunc HasRange(node Positioner) bool {\n\t// we don't know if getRange will be called with shortRange set to\n\t// true, so make sure that both work.\n\t_, _, ok := getRange(node, false)\n\tif !ok {\n\t\treturn false\n\t}\n\t_, _, ok = getRange(node, true)\n\treturn ok\n}\n\nfunc getRange(node Positioner, short bool) (pos, end token.Pos, ok bool) {\n\tswitch n := node.(type) {\n\tcase sourcer:\n\t\ts := n.Source()\n\t\tif s == nil {\n\t\t\treturn 0, 0, false\n\t\t}\n\t\tif short {\n\t\t\tp, e := shortRange(s)\n\t\t\treturn p, e, true\n\t\t}\n\t\treturn s.Pos(), s.End(), true\n\tcase fullPositioner:\n\t\tif short {\n\t\t\tp, e := shortRange(n)\n\t\t\treturn p, e, true\n\t\t}\n\t\treturn n.Pos(), n.End(), true\n\tdefault:\n\t\treturn n.Pos(), token.NoPos, true\n\t}\n}\n\nfunc Report(pass *analysis.Pass, node Positioner, message string, opts ...Option) {\n\tcfg := &Options{}\n\tfor _, opt := range opts {\n\t\topt(cfg)\n\t}\n\n\tlangVersion := code.LanguageVersion(pass, node)\n\tstdlibVersion := code.StdlibVersion(pass, node)\n\tif n := cfg.MaximumLanguageVersion; n != \"\" && version.Compare(n, langVersion) == -1 {\n\t\treturn\n\t}\n\tif n := cfg.MaximumStdlibVersion; n != \"\" && version.Compare(n, stdlibVersion) == -1 {\n\t\treturn\n\t}\n\tif n := cfg.MinimumLanguageVersion; n != \"\" && version.Compare(n, langVersion) == 1 {\n\t\treturn\n\t}\n\tif n := cfg.MinimumStdlibVersion; n != \"\" && version.Compare(n, stdlibVersion) == 1 {\n\t\treturn\n\t}\n\n\tfile := DisplayPosition(pass.Fset, node.Pos()).Filename\n\tif cfg.FilterGenerated {\n\t\tm := pass.ResultOf[generated.Analyzer].(map[string]generated.Generator)\n\t\tif _, ok := m[file]; ok {\n\t\t\treturn\n\t\t}\n\t}\n\n\tpos, end, ok := getRange(node, cfg.ShortRange)\n\tif !ok {\n\t\tpanic(fmt.Sprintf(\"no valid position for reporting node %v\", node))\n\t}\n\td := analysis.Diagnostic{\n\t\tPos:            pos,\n\t\tEnd:            end,\n\t\tMessage:        message,\n\t\tSuggestedFixes: cfg.Fixes,\n\t\tRelated:        cfg.Related,\n\t}\n\tpass.Report(d)\n}\n\nfunc Render(pass *analysis.Pass, x any) string {\n\tvar buf bytes.Buffer\n\tif err := format.Node(&buf, pass.Fset, x); err != nil {\n\t\tpanic(err)\n\t}\n\treturn buf.String()\n}\n\nfunc RenderArgs(pass *analysis.Pass, args []ast.Expr) string {\n\tvar ss []string\n\tfor _, arg := range args {\n\t\tss = append(ss, Render(pass, arg))\n\t}\n\treturn strings.Join(ss, \", \")\n}\n\nfunc DisplayPosition(fset *token.FileSet, p token.Pos) token.Position {\n\tif p == token.NoPos {\n\t\treturn token.Position{}\n\t}\n\n\t// Only use the adjusted position if it points to another Go file.\n\t// This means we'll point to the original file for cgo files, but\n\t// we won't point to a YACC grammar file.\n\tpos := fset.PositionFor(p, false)\n\tadjPos := fset.PositionFor(p, true)\n\n\tif filepath.Ext(adjPos.Filename) == \".go\" {\n\t\treturn adjPos\n\t}\n\n\treturn pos\n}\n\nfunc Ordinal(n int) string {\n\tsuffix := \"th\"\n\tif n < 10 || n > 20 {\n\t\tswitch n % 10 {\n\t\tcase 0:\n\t\t\tsuffix = \"th\"\n\t\tcase 1:\n\t\t\tsuffix = \"st\"\n\t\tcase 2:\n\t\t\tsuffix = \"nd\"\n\t\tcase 3:\n\t\t\tsuffix = \"rd\"\n\t\tdefault:\n\t\t\tsuffix = \"th\"\n\t\t}\n\t}\n\n\treturn strconv.Itoa(n) + suffix\n}\n"
  },
  {
    "path": "analysis/report/report_test.go",
    "content": "package report\n\nimport \"testing\"\n\nfunc TestOrdinal(t *testing.T) {\n\ttests := []struct {\n\t\tnum  int\n\t\twant string\n\t}{\n\t\t{0, \"0th\"}, {1, \"1st\"}, {2, \"2nd\"}, {3, \"3rd\"}, {4, \"4th\"}, {5, \"5th\"}, {6, \"6th\"}, {7, \"7th\"}, {8, \"8th\"}, {9, \"9th\"},\n\t\t{10, \"10th\"}, {11, \"11th\"}, {12, \"12th\"}, {13, \"13th\"}, {14, \"14th\"}, {15, \"15th\"}, {16, \"16th\"}, {17, \"17th\"}, {18, \"18th\"}, {19, \"19th\"},\n\t\t{20, \"20th\"}, {21, \"21st\"}, {22, \"22nd\"}, {23, \"23rd\"}, {24, \"24th\"}, {25, \"25th\"}, {26, \"26th\"}, {27, \"27th\"}, {28, \"28th\"}, {29, \"29th\"},\n\t}\n\tfor _, tt := range tests {\n\t\tif got := Ordinal(tt.num); got != tt.want {\n\t\t\tt.Errorf(\"Ordinal(%d) = %s, want %s\", tt.num, got, tt.want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cmd/staticcheck/README.md",
    "content": "# staticcheck\n\n_staticcheck_ offers extensive analysis of Go code, covering a myriad\nof categories. It will detect bugs, suggest code simplifications,\npoint out dead code, and more.\n\n## Installation\n\nSee [the main README](https://github.com/dominikh/go-tools#installation) for installation instructions.\n\n## Documentation\n\nDetailed documentation can be found on\n[staticcheck.dev](https://staticcheck.dev/docs/).\n\n"
  },
  {
    "path": "cmd/staticcheck/staticcheck.go",
    "content": "// staticcheck analyses Go code and makes it better.\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"honnef.co/go/tools/lintcmd\"\n\t\"honnef.co/go/tools/lintcmd/version\"\n\t\"honnef.co/go/tools/quickfix\"\n\t\"honnef.co/go/tools/simple\"\n\t\"honnef.co/go/tools/staticcheck\"\n\t\"honnef.co/go/tools/stylecheck\"\n\t\"honnef.co/go/tools/unused\"\n)\n\nfunc main() {\n\tcmd := lintcmd.NewCommand(\"staticcheck\")\n\tcmd.SetVersion(version.Version, version.MachineVersion)\n\n\tfs := cmd.FlagSet()\n\tdebug := fs.String(\"debug.unused-graph\", \"\", \"Write unused's object graph to `file`\")\n\tqf := fs.Bool(\"debug.run-quickfix-analyzers\", false, \"Run quickfix analyzers\")\n\n\tcmd.ParseFlags(os.Args[1:])\n\n\tcmd.AddAnalyzers(simple.Analyzers...)\n\tcmd.AddAnalyzers(staticcheck.Analyzers...)\n\tcmd.AddAnalyzers(stylecheck.Analyzers...)\n\tcmd.AddAnalyzers(unused.Analyzer)\n\n\tif *qf {\n\t\tcmd.AddAnalyzers(quickfix.Analyzers...)\n\t}\n\n\tif *debug != \"\" {\n\t\tf, err := os.OpenFile(*debug, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tunused.Debug = f\n\t}\n\n\tcmd.Run()\n}\n"
  },
  {
    "path": "cmd/structlayout/README.md",
    "content": "# structlayout\n\nThe _structlayout_ utility prints the layout of a struct – that is the\nbyte offset and size of each field, respecting alignment/padding.\n\nThe information is printed in human-readable form by default, but can\nbe emitted as JSON with the `-json` flag. This makes it easy to\nconsume this information in other tools.\n\nA utility called _structlayout-pretty_ takes this JSON and prints an\nASCII graphic representing the memory layout.\n\n_structlayout-optimize_ is another tool. Inspired by\n[maligned](https://github.com/mdempsky/maligned), it reads\n_structlayout_ JSON on stdin and reorders fields to minimize the\namount of padding. The tool can itself emit JSON and feed into e.g.\n_structlayout-pretty_.\n\n_structlayout-svg_ is a third-party tool that, similarly to\n_structlayout-pretty_, visualises struct layouts. It does so by\ngenerating a fancy-looking SVG graphic. You can install it via\n\n```\ngo get github.com/ajstarks/svgo/structlayout-svg\n```\n\n## Installation\n\nSee [the main README](https://github.com/dominikh/go-tools#installation) for installation instructions.\n\n## Examples\n\n```\n$ structlayout bufio Reader\nReader.buf []byte: 0-24 (24 bytes)\nReader.rd io.Reader: 24-40 (16 bytes)\nReader.r int: 40-48 (8 bytes)\nReader.w int: 48-56 (8 bytes)\nReader.err error: 56-72 (16 bytes)\nReader.lastByte int: 72-80 (8 bytes)\nReader.lastRuneSize int: 80-88 (8 bytes)\n```\n\n```\n$ structlayout -json bufio Reader | jq .\n[\n  {\n    \"name\": \"Reader.buf\",\n    \"type\": \"[]byte\",\n    \"start\": 0,\n    \"end\": 24,\n    \"size\": 24,\n    \"is_padding\": false\n  },\n  {\n    \"name\": \"Reader.rd\",\n    \"type\": \"io.Reader\",\n    \"start\": 24,\n    \"end\": 40,\n    \"size\": 16,\n    \"is_padding\": false\n  },\n  {\n    \"name\": \"Reader.r\",\n    \"type\": \"int\",\n    \"start\": 40,\n    \"end\": 48,\n    \"size\": 8,\n    \"is_padding\": false\n  },\n...\n```\n\n```\n$ structlayout -json bufio Reader | structlayout-pretty \n    +--------+\n  0 |        | <- Reader.buf []byte\n    +--------+\n    -........-\n    +--------+\n 23 |        |\n    +--------+\n 24 |        | <- Reader.rd io.Reader\n    +--------+\n    -........-\n    +--------+\n 39 |        |\n    +--------+\n 40 |        | <- Reader.r int\n    +--------+\n    -........-\n    +--------+\n 47 |        |\n    +--------+\n 48 |        | <- Reader.w int\n    +--------+\n    -........-\n    +--------+\n 55 |        |\n    +--------+\n 56 |        | <- Reader.err error\n    +--------+\n    -........-\n    +--------+\n 71 |        |\n    +--------+\n 72 |        | <- Reader.lastByte int\n    +--------+\n    -........-\n    +--------+\n 79 |        |\n    +--------+\n 80 |        | <- Reader.lastRuneSize int\n    +--------+\n    -........-\n    +--------+\n 87 |        |\n    +--------+\n```\n\n```\n$ structlayout -json bytes Buffer | structlayout-svg -t \"bytes.Buffer\" > /tmp/struct.svg\n```\n\n![memory layout of bytes.Buffer](/images/screenshots/struct.png)\n"
  },
  {
    "path": "cmd/structlayout/main.go",
    "content": "// structlayout displays the layout (field sizes and padding) of structs.\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"go/build\"\n\t\"go/types\"\n\t\"log\"\n\t\"os\"\n\n\t\"honnef.co/go/tools/go/gcsizes\"\n\t\"honnef.co/go/tools/lintcmd/version\"\n\tst \"honnef.co/go/tools/structlayout\"\n\n\t\"golang.org/x/tools/go/packages\"\n)\n\nvar (\n\tfJSON    bool\n\tfVersion bool\n)\n\nfunc init() {\n\tflag.BoolVar(&fJSON, \"json\", false, \"Format data as JSON\")\n\tflag.BoolVar(&fVersion, \"version\", false, \"Print version and exit\")\n}\n\nfunc main() {\n\tlog.SetFlags(0)\n\tflag.Parse()\n\n\tif fVersion {\n\t\tversion.Print(version.Version, version.MachineVersion)\n\t\tos.Exit(0)\n\t}\n\n\tif len(flag.Args()) != 2 {\n\t\tflag.Usage()\n\t\tos.Exit(1)\n\t}\n\n\tcfg := &packages.Config{\n\t\tMode:  packages.NeedImports | packages.NeedExportFile | packages.NeedTypes | packages.NeedSyntax,\n\t\tTests: true,\n\t}\n\tpkgs, err := packages.Load(cfg, flag.Args()[0])\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfor _, pkg := range pkgs {\n\t\ttypName := flag.Args()[1]\n\n\t\tvar typ types.Type\n\t\tobj := pkg.Types.Scope().Lookup(typName)\n\t\tif obj == nil {\n\t\t\tcontinue\n\t\t}\n\t\ttyp = obj.Type()\n\n\t\tst, ok := typ.Underlying().(*types.Struct)\n\t\tif !ok {\n\t\t\tlog.Fatal(\"identifier is not a struct type\")\n\t\t}\n\n\t\tfields := sizes(st, types.Unalias(typ).(*types.Named).Obj().Name(), 0, nil)\n\t\tif fJSON {\n\t\t\temitJSON(fields)\n\t\t} else {\n\t\t\temitText(fields)\n\t\t}\n\t\treturn\n\t}\n\n\tlog.Fatal(\"couldn't find type\")\n}\n\nfunc emitJSON(fields []st.Field) {\n\tif fields == nil {\n\t\tfields = []st.Field{}\n\t}\n\tjson.NewEncoder(os.Stdout).Encode(fields)\n}\n\nfunc emitText(fields []st.Field) {\n\tfor _, field := range fields {\n\t\tfmt.Println(field)\n\t}\n}\nfunc sizes(typ *types.Struct, prefix string, base int64, out []st.Field) []st.Field {\n\ts := gcsizes.ForArch(build.Default.GOARCH)\n\tn := typ.NumFields()\n\tvar fields []*types.Var\n\tfor i := range n {\n\t\tfields = append(fields, typ.Field(i))\n\t}\n\toffsets := s.Offsetsof(fields)\n\tfor i := range offsets {\n\t\toffsets[i] += base\n\t}\n\n\tpos := base\n\tfor i, field := range fields {\n\t\tif offsets[i] > pos {\n\t\t\tpadding := offsets[i] - pos\n\t\t\tout = append(out, st.Field{\n\t\t\t\tIsPadding: true,\n\t\t\t\tStart:     pos,\n\t\t\t\tEnd:       pos + padding,\n\t\t\t\tSize:      padding,\n\t\t\t})\n\t\t\tpos += padding\n\t\t}\n\t\tsize := s.Sizeof(field.Type())\n\t\tif typ2, ok := field.Type().Underlying().(*types.Struct); ok && typ2.NumFields() != 0 {\n\t\t\tout = sizes(typ2, prefix+\".\"+field.Name(), pos, out)\n\t\t} else {\n\t\t\tout = append(out, st.Field{\n\t\t\t\tName:  prefix + \".\" + field.Name(),\n\t\t\t\tType:  field.Type().String(),\n\t\t\t\tStart: offsets[i],\n\t\t\t\tEnd:   offsets[i] + size,\n\t\t\t\tSize:  size,\n\t\t\t\tAlign: s.Alignof(field.Type()),\n\t\t\t})\n\t\t}\n\t\tpos += size\n\t}\n\n\tif len(out) == 0 {\n\t\treturn out\n\t}\n\tfield := &out[len(out)-1]\n\tif field.Size == 0 {\n\t\tfield.Size = 1\n\t\tfield.End++\n\t}\n\tpad := s.Sizeof(typ) - field.End\n\tif pad > 0 {\n\t\tout = append(out, st.Field{\n\t\t\tIsPadding: true,\n\t\t\tStart:     field.End,\n\t\t\tEnd:       field.End + pad,\n\t\t\tSize:      pad,\n\t\t})\n\t}\n\n\treturn out\n}\n"
  },
  {
    "path": "cmd/structlayout-optimize/main.go",
    "content": "// structlayout-optimize reorders struct fields to minimize the amount\n// of padding.\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/lintcmd/version\"\n\tst \"honnef.co/go/tools/structlayout\"\n)\n\nvar (\n\tfJSON    bool\n\tfRecurse bool\n\tfVersion bool\n)\n\nfunc init() {\n\tflag.BoolVar(&fJSON, \"json\", false, \"Format data as JSON\")\n\tflag.BoolVar(&fRecurse, \"r\", false, \"Break up structs and reorder their fields freely\")\n\tflag.BoolVar(&fVersion, \"version\", false, \"Print version and exit\")\n}\n\nfunc main() {\n\tlog.SetFlags(0)\n\tflag.Parse()\n\n\tif fVersion {\n\t\tversion.Print(version.Version, version.MachineVersion)\n\t\tos.Exit(0)\n\t}\n\n\tvar in []st.Field\n\tif err := json.NewDecoder(os.Stdin).Decode(&in); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tif len(in) == 0 {\n\t\treturn\n\t}\n\tif !fRecurse {\n\t\tin = combine(in)\n\t}\n\tvar fields []st.Field\n\tfor _, field := range in {\n\t\tif field.IsPadding {\n\t\t\tcontinue\n\t\t}\n\t\tfields = append(fields, field)\n\t}\n\toptimize(fields)\n\tfields = pad(fields)\n\n\tif fJSON {\n\t\tjson.NewEncoder(os.Stdout).Encode(fields)\n\t} else {\n\t\tfor _, field := range fields {\n\t\t\tfmt.Println(field)\n\t\t}\n\t}\n}\n\nfunc combine(fields []st.Field) []st.Field {\n\tnew := st.Field{}\n\tcur := \"\"\n\tvar out []st.Field\n\twasPad := true\n\tfor _, field := range fields {\n\t\tvar prefix string\n\t\tif field.IsPadding {\n\t\t\twasPad = true\n\t\t\tcontinue\n\t\t}\n\t\tp := strings.Split(field.Name, \".\")\n\t\tprefix = strings.Join(p[:2], \".\")\n\t\tif field.Align > new.Align {\n\t\t\tnew.Align = field.Align\n\t\t}\n\t\tif !wasPad {\n\t\t\tnew.End = field.Start\n\t\t\tnew.Size = new.End - new.Start\n\t\t}\n\t\tif prefix != cur {\n\t\t\tif cur != \"\" {\n\t\t\t\tout = append(out, new)\n\t\t\t}\n\t\t\tcur = prefix\n\t\t\tnew = field\n\t\t\tnew.Name = prefix\n\t\t} else {\n\t\t\tnew.Type = \"struct\"\n\t\t}\n\t\twasPad = false\n\t}\n\tnew.Size = new.End - new.Start\n\tout = append(out, new)\n\treturn out\n}\n\nfunc optimize(fields []st.Field) {\n\tsort.Sort(&byAlignAndSize{fields})\n}\n\nfunc pad(fields []st.Field) []st.Field {\n\tif len(fields) == 0 {\n\t\treturn nil\n\t}\n\tvar out []st.Field\n\tpos := int64(0)\n\toffsets := offsetsof(fields)\n\talignment := int64(1)\n\tfor i, field := range fields {\n\t\tif field.Align > alignment {\n\t\t\talignment = field.Align\n\t\t}\n\t\tif offsets[i] > pos {\n\t\t\tpadding := offsets[i] - pos\n\t\t\tout = append(out, st.Field{\n\t\t\t\tIsPadding: true,\n\t\t\t\tStart:     pos,\n\t\t\t\tEnd:       pos + padding,\n\t\t\t\tSize:      padding,\n\t\t\t})\n\t\t\tpos += padding\n\t\t}\n\t\tfield.Start = pos\n\t\tfield.End = pos + field.Size\n\t\tout = append(out, field)\n\t\tpos += field.Size\n\t}\n\tsz := size(out)\n\tpad := align(sz, alignment) - sz\n\tif pad > 0 {\n\t\tfield := out[len(out)-1]\n\t\tout = append(out, st.Field{\n\t\t\tIsPadding: true,\n\t\t\tStart:     field.End,\n\t\t\tEnd:       field.End + pad,\n\t\t\tSize:      pad,\n\t\t})\n\t}\n\treturn out\n}\n\nfunc size(fields []st.Field) int64 {\n\tn := int64(0)\n\tfor _, field := range fields {\n\t\tn += field.Size\n\t}\n\treturn n\n}\n\ntype byAlignAndSize struct {\n\tfields []st.Field\n}\n\nfunc (s *byAlignAndSize) Len() int { return len(s.fields) }\nfunc (s *byAlignAndSize) Swap(i, j int) {\n\ts.fields[i], s.fields[j] = s.fields[j], s.fields[i]\n}\n\nfunc (s *byAlignAndSize) Less(i, j int) bool {\n\t// Place zero sized objects before non-zero sized objects.\n\tif s.fields[i].Size == 0 && s.fields[j].Size != 0 {\n\t\treturn true\n\t}\n\tif s.fields[j].Size == 0 && s.fields[i].Size != 0 {\n\t\treturn false\n\t}\n\n\t// Next, place more tightly aligned objects before less tightly aligned objects.\n\tif s.fields[i].Align != s.fields[j].Align {\n\t\treturn s.fields[i].Align > s.fields[j].Align\n\t}\n\n\t// Lastly, order by size.\n\tif s.fields[i].Size != s.fields[j].Size {\n\t\treturn s.fields[i].Size > s.fields[j].Size\n\t}\n\n\treturn false\n}\n\nfunc offsetsof(fields []st.Field) []int64 {\n\toffsets := make([]int64, len(fields))\n\tvar o int64\n\tfor i, f := range fields {\n\t\ta := f.Align\n\t\to = align(o, a)\n\t\toffsets[i] = o\n\t\to += f.Size\n\t}\n\treturn offsets\n}\n\n// align returns the smallest y >= x such that y % a == 0.\nfunc align(x, a int64) int64 {\n\ty := x + a - 1\n\treturn y - y%a\n}\n"
  },
  {
    "path": "cmd/structlayout-pretty/main.go",
    "content": "// structlayout-pretty formats the output of structlayout with ASCII\n// art.\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/lintcmd/version\"\n\tst \"honnef.co/go/tools/structlayout\"\n)\n\nvar (\n\tfVerbose bool\n\tfVersion bool\n)\n\nfunc init() {\n\tflag.BoolVar(&fVerbose, \"v\", false, \"Do not compact consecutive bytes of fields\")\n\tflag.BoolVar(&fVersion, \"version\", false, \"Print version and exit\")\n}\n\nfunc main() {\n\tlog.SetFlags(0)\n\tflag.Parse()\n\n\tif fVersion {\n\t\tversion.Print(version.Version, version.MachineVersion)\n\t\tos.Exit(0)\n\t}\n\n\tvar fields []st.Field\n\tif err := json.NewDecoder(os.Stdin).Decode(&fields); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tif len(fields) == 0 {\n\t\treturn\n\t}\n\tmax := fields[len(fields)-1].End\n\tmaxLength := len(fmt.Sprintf(\"%d\", max))\n\tpadding := strings.Repeat(\" \", maxLength+2)\n\tformat := fmt.Sprintf(\" %%%dd \", maxLength)\n\tpos := int64(0)\n\tfmt.Println(padding + \"+--------+\")\n\tfor _, field := range fields {\n\t\tname := field.Name + \" \" + field.Type\n\t\tif field.IsPadding {\n\t\t\tname = \"padding\"\n\t\t}\n\t\tfmt.Printf(format+\"|        | <- %s (size %d, align %d)\\n\", pos, name, field.Size, field.Align)\n\t\tfmt.Println(padding + \"+--------+\")\n\n\t\tif fVerbose {\n\t\t\tfor i := int64(0); i < field.Size-1; i++ {\n\t\t\t\tfmt.Printf(format+\"|        |\\n\", pos+i+1)\n\t\t\t\tfmt.Println(padding + \"+--------+\")\n\t\t\t}\n\t\t} else {\n\t\t\tif field.Size > 2 {\n\t\t\t\tfmt.Println(padding + \"-........-\")\n\t\t\t\tfmt.Println(padding + \"+--------+\")\n\t\t\t\tfmt.Printf(format+\"|        |\\n\", pos+field.Size-1)\n\t\t\t\tfmt.Println(padding + \"+--------+\")\n\t\t\t}\n\t\t}\n\t\tpos += field.Size\n\t}\n}\n"
  },
  {
    "path": "config/config.go",
    "content": "package config\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/BurntSushi/toml\"\n\t\"golang.org/x/tools/go/analysis\"\n)\n\n// Dir looks at a list of absolute file names, which should make up a\n// single package, and returns the path of the directory that may\n// contain a staticcheck.conf file. It returns the empty string if no\n// such directory could be determined, for example because all files\n// were located in Go's build cache.\nfunc Dir(files []string) string {\n\tif len(files) == 0 {\n\t\treturn \"\"\n\t}\n\tcache, err := os.UserCacheDir()\n\tif err != nil {\n\t\tcache = \"\"\n\t}\n\tvar path string\n\tfor _, p := range files {\n\t\t// FIXME(dh): using strings.HasPrefix isn't technically\n\t\t// correct, but it should be good enough for now.\n\t\tif cache != \"\" && strings.HasPrefix(p, cache) {\n\t\t\t// File in the build cache of the standard Go build system\n\t\t\tcontinue\n\t\t}\n\t\tpath = p\n\t\tbreak\n\t}\n\n\tif path == \"\" {\n\t\t// The package only consists of generated files.\n\t\treturn \"\"\n\t}\n\n\tdir := filepath.Dir(path)\n\treturn dir\n}\n\nfunc dirAST(files []*ast.File, fset *token.FileSet) string {\n\tnames := make([]string, len(files))\n\tfor i, f := range files {\n\t\tnames[i] = fset.PositionFor(f.Pos(), true).Filename\n\t}\n\treturn Dir(names)\n}\n\nvar Analyzer = &analysis.Analyzer{\n\tName: \"config\",\n\tDoc:  \"loads configuration for the current package tree\",\n\tRun: func(pass *analysis.Pass) (any, error) {\n\t\tdir := dirAST(pass.Files, pass.Fset)\n\t\tif dir == \"\" {\n\t\t\tcfg := DefaultConfig\n\t\t\treturn &cfg, nil\n\t\t}\n\t\tcfg, err := Load(dir)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error loading staticcheck.conf: %s\", err)\n\t\t}\n\t\treturn &cfg, nil\n\t},\n\tRunDespiteErrors: true,\n\tResultType:       reflect.TypeFor[*Config](),\n}\n\nfunc For(pass *analysis.Pass) *Config {\n\treturn pass.ResultOf[Analyzer].(*Config)\n}\n\nfunc mergeLists(a, b []string) []string {\n\tout := make([]string, 0, len(a)+len(b))\n\tfor _, el := range b {\n\t\tif el == \"inherit\" {\n\t\t\tout = append(out, a...)\n\t\t} else {\n\t\t\tout = append(out, el)\n\t\t}\n\t}\n\n\treturn out\n}\n\nfunc normalizeList(list []string) []string {\n\tif len(list) > 1 {\n\t\tnlist := make([]string, 0, len(list))\n\t\tnlist = append(nlist, list[0])\n\t\tfor i, el := range list[1:] {\n\t\t\tif el != list[i] {\n\t\t\t\tnlist = append(nlist, el)\n\t\t\t}\n\t\t}\n\t\tlist = nlist\n\t}\n\n\tfor _, el := range list {\n\t\tif el == \"inherit\" {\n\t\t\t// This should never happen, because the default config\n\t\t\t// should not use \"inherit\"\n\t\t\tpanic(`unresolved \"inherit\"`)\n\t\t}\n\t}\n\n\treturn list\n}\n\nfunc (cfg Config) Merge(ocfg Config) Config {\n\tif ocfg.Checks != nil {\n\t\tcfg.Checks = mergeLists(cfg.Checks, ocfg.Checks)\n\t}\n\tif ocfg.Initialisms != nil {\n\t\tcfg.Initialisms = mergeLists(cfg.Initialisms, ocfg.Initialisms)\n\t}\n\tif ocfg.DotImportWhitelist != nil {\n\t\tcfg.DotImportWhitelist = mergeLists(cfg.DotImportWhitelist, ocfg.DotImportWhitelist)\n\t}\n\tif ocfg.HTTPStatusCodeWhitelist != nil {\n\t\tcfg.HTTPStatusCodeWhitelist = mergeLists(cfg.HTTPStatusCodeWhitelist, ocfg.HTTPStatusCodeWhitelist)\n\t}\n\treturn cfg\n}\n\ntype Config struct {\n\t// TODO(dh): this implementation makes it impossible for external\n\t// clients to add their own checkers with configuration. At the\n\t// moment, we don't really care about that; we don't encourage\n\t// that people use this package. In the future, we may. The\n\t// obvious solution would be using map[string]interface{}, but\n\t// that's obviously subpar.\n\n\tChecks                  []string `toml:\"checks\"`\n\tInitialisms             []string `toml:\"initialisms\"`\n\tDotImportWhitelist      []string `toml:\"dot_import_whitelist\"`\n\tHTTPStatusCodeWhitelist []string `toml:\"http_status_code_whitelist\"`\n}\n\nfunc (c Config) String() string {\n\tbuf := &bytes.Buffer{}\n\n\tfmt.Fprintf(buf, \"Checks: %#v\\n\", c.Checks)\n\tfmt.Fprintf(buf, \"Initialisms: %#v\\n\", c.Initialisms)\n\tfmt.Fprintf(buf, \"DotImportWhitelist: %#v\\n\", c.DotImportWhitelist)\n\tfmt.Fprintf(buf, \"HTTPStatusCodeWhitelist: %#v\", c.HTTPStatusCodeWhitelist)\n\n\treturn buf.String()\n}\n\n// DefaultConfig is the default configuration.\n// Its initial value describes the majority of the default configuration,\n// but the Checks field can be updated at runtime based on the analyzers being used, to disable non-default checks.\n// For cmd/staticcheck, this is handled by (*lintcmd.Command).Run.\n//\n// Note that DefaultConfig shouldn't be modified while analyzers are executing.\nvar DefaultConfig = Config{\n\tChecks: []string{\"all\"},\n\tInitialisms: []string{\n\t\t\"ACL\", \"API\", \"ASCII\", \"CPU\", \"CSS\", \"DNS\",\n\t\t\"EOF\", \"GUID\", \"HTML\", \"HTTP\", \"HTTPS\", \"ID\",\n\t\t\"IP\", \"JSON\", \"QPS\", \"RAM\", \"RPC\", \"SLA\",\n\t\t\"SMTP\", \"SQL\", \"SSH\", \"TCP\", \"TLS\", \"TTL\",\n\t\t\"UDP\", \"UI\", \"GID\", \"UID\", \"UUID\", \"URI\",\n\t\t\"URL\", \"UTF8\", \"VM\", \"XML\", \"XMPP\", \"XSRF\",\n\t\t\"XSS\", \"SIP\", \"RTP\", \"AMQP\", \"DB\", \"TS\",\n\t},\n\tDotImportWhitelist: []string{\n\t\t\"simd/archsimd\",\n\t\t\"github.com/mmcloughlin/avo/build\",\n\t\t\"github.com/mmcloughlin/avo/operand\",\n\t\t\"github.com/mmcloughlin/avo/reg\",\n\t},\n\tHTTPStatusCodeWhitelist: []string{\"200\", \"400\", \"404\", \"500\"},\n}\n\nconst ConfigName = \"staticcheck.conf\"\n\ntype ParseError struct {\n\tFilename string\n\ttoml.ParseError\n}\n\nfunc parseConfigs(dir string) ([]Config, error) {\n\tvar out []Config\n\n\t// TODO(dh): consider stopping at the GOPATH/module boundary\n\tfor dir != \"\" {\n\t\tpath := filepath.Join(dir, ConfigName)\n\t\tfi, err := os.Stat(path)\n\t\tif os.IsNotExist(err) || (err == nil && !fi.Mode().IsRegular()) {\n\t\t\t// walk up\n\t\t\tndir := filepath.Dir(dir)\n\t\t\tif ndir == dir {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdir = ndir\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// There is a small TOCTOU window here, but we're fine with reporting an\n\t\t// error if the source tree is modified concurrently in weird ways while\n\t\t// running Staticcheck.\n\t\tf, err := os.Open(path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar cfg Config\n\t\t_, err = toml.NewDecoder(f).Decode(&cfg)\n\t\tf.Close()\n\t\tif err != nil {\n\t\t\tif err, ok := err.(toml.ParseError); ok {\n\t\t\t\treturn nil, ParseError{\n\t\t\t\t\tFilename:   filepath.Join(dir, ConfigName),\n\t\t\t\t\tParseError: err,\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\t\tout = append(out, cfg)\n\t\tndir := filepath.Dir(dir)\n\t\tif ndir == dir {\n\t\t\tbreak\n\t\t}\n\t\tdir = ndir\n\t}\n\tout = append(out, DefaultConfig)\n\tif len(out) < 2 {\n\t\treturn out, nil\n\t}\n\tfor i := 0; i < len(out)/2; i++ {\n\t\tout[i], out[len(out)-1-i] = out[len(out)-1-i], out[i]\n\t}\n\treturn out, nil\n}\n\nfunc mergeConfigs(confs []Config) Config {\n\tif len(confs) == 0 {\n\t\t// This shouldn't happen because we always have at least a\n\t\t// default config.\n\t\tpanic(\"trying to merge zero configs\")\n\t}\n\tif len(confs) == 1 {\n\t\treturn confs[0]\n\t}\n\tconf := confs[0]\n\tfor _, oconf := range confs[1:] {\n\t\tconf = conf.Merge(oconf)\n\t}\n\treturn conf\n}\n\nfunc Load(dir string) (Config, error) {\n\tconfs, err := parseConfigs(dir)\n\tif err != nil {\n\t\treturn Config{}, err\n\t}\n\tconf := mergeConfigs(confs)\n\n\tconf.Checks = normalizeList(conf.Checks)\n\tconf.Initialisms = normalizeList(conf.Initialisms)\n\tconf.DotImportWhitelist = normalizeList(conf.DotImportWhitelist)\n\tconf.HTTPStatusCodeWhitelist = normalizeList(conf.HTTPStatusCodeWhitelist)\n\n\treturn conf, nil\n}\n"
  },
  {
    "path": "config/example.conf",
    "content": "checks = [\"all\", \"-SA9003\", \"-ST1000\", \"-ST1003\", \"-ST1016\", \"-ST1020\", \"-ST1021\", \"-ST1022\", \"-ST1023\"]\ninitialisms = [\"ACL\", \"API\", \"ASCII\", \"CPU\", \"CSS\", \"DNS\",\n\t\"EOF\", \"GUID\", \"HTML\", \"HTTP\", \"HTTPS\", \"ID\",\n\t\"IP\", \"JSON\", \"QPS\", \"RAM\", \"RPC\", \"SLA\",\n\t\"SMTP\", \"SQL\", \"SSH\", \"TCP\", \"TLS\", \"TTL\",\n\t\"UDP\", \"UI\", \"GID\", \"UID\", \"UUID\", \"URI\",\n\t\"URL\", \"UTF8\", \"VM\", \"XML\", \"XMPP\", \"XSRF\",\n\t\"XSS\", \"SIP\", \"RTP\", \"AMQP\", \"DB\", \"TS\"]\ndot_import_whitelist = [\n    \"simd/archsimd\",\n    \"github.com/mmcloughlin/avo/build\",\n    \"github.com/mmcloughlin/avo/operand\",\n    \"github.com/mmcloughlin/avo/reg\",\n]\nhttp_status_code_whitelist = [\"200\", \"400\", \"404\", \"500\"]\n"
  },
  {
    "path": "debug/debug.go",
    "content": "// Package debug contains helpers for debugging static analyses.\npackage debug\n\nimport (\n\t\"bytes\"\n\t\"go/ast\"\n\t\"go/format\"\n\t\"go/importer\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"sync\"\n)\n\n// TypeCheck parses and type-checks a single-file Go package from a string.\n// The package must not have any imports.\nfunc TypeCheck(src string) (*ast.File, *types.Package, *types.Info, error) {\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"foo.go\", src, parser.ParseComments|parser.SkipObjectResolution)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tpkg := types.NewPackage(\"foo\", f.Name.Name)\n\tinfo := &types.Info{\n\t\tTypes:      map[ast.Expr]types.TypeAndValue{},\n\t\tDefs:       map[*ast.Ident]types.Object{},\n\t\tUses:       map[*ast.Ident]types.Object{},\n\t\tImplicits:  map[ast.Node]types.Object{},\n\t\tSelections: map[*ast.SelectorExpr]*types.Selection{},\n\t\tScopes:     map[ast.Node]*types.Scope{},\n\t\tInitOrder:  []*types.Initializer{},\n\t\tInstances:  map[*ast.Ident]types.Instance{},\n\t}\n\ttcfg := &types.Config{\n\t\tImporter: importer.Default(),\n\t}\n\tif err := types.NewChecker(tcfg, fset, pkg, info).Files([]*ast.File{f}); err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\treturn f, pkg, info, nil\n}\n\nfunc FormatNode(node ast.Node) string {\n\tvar buf bytes.Buffer\n\tfset := token.NewFileSet()\n\tformat.Node(&buf, fset, node)\n\treturn buf.String()\n}\n\nvar aliasesDefaultOnce sync.Once\nvar gotypesaliasDefault bool\n\nfunc AliasesEnabled() bool {\n\t// Dynamically check if Aliases will be produced from go/types.\n\taliasesDefaultOnce.Do(func() {\n\t\tfset := token.NewFileSet()\n\t\tf, _ := parser.ParseFile(fset, \"a.go\", \"package p; type A = int\", 0)\n\t\tpkg, _ := new(types.Config).Check(\"p\", fset, []*ast.File{f}, nil)\n\t\t_, gotypesaliasDefault = pkg.Scope().Lookup(\"A\").Type().(*types.Alias)\n\t})\n\treturn gotypesaliasDefault\n}\n"
  },
  {
    "path": "dist/build.sh",
    "content": "#!/bin/sh -e\n\n\nbuild() {\n    ROOT=\"$GOPATH/src/honnef.co/go/tools\"\n\n    os=\"$1\"\n    arch=\"$2\"\n\n    echo \"Building GOOS=$os GOARCH=$arch...\"\n    exe=\"staticcheck\"\n    if [ $os = \"windows\" ]; then\n        exe=\"${exe}.exe\"\n    fi\n    target=\"staticcheck_${os}_${arch}\"\n\n    arm=\"\"\n    case \"$arch\" in\n        armv5l)\n            arm=5\n            arch=arm\n            ;;\n        armv6l)\n            arm=6\n            arch=arm\n            ;;\n        armv7l)\n            arm=7\n            arch=arm\n            ;;\n        arm64)\n            arch=arm64\n            ;;\n    esac\n\n    mkdir \"$d/staticcheck\"\n    cp \"$ROOT/LICENSE\" \"$ROOT/LICENSE-THIRD-PARTY\" \"$d/staticcheck\"\n    CGO_ENABLED=0 GOOS=$os GOARCH=$arch GOARM=$arm GO111MODULE=on go build -trimpath -o \"$d/staticcheck/$exe\" honnef.co/go/tools/cmd/staticcheck\n    (\n        cd \"$d\"\n        tar -czf \"$target.tar.gz\" staticcheck\n        sha256sum \"$target.tar.gz\" > \"$target.tar.gz.sha256\"\n    )\n    rm -rf \"$d/staticcheck\"\n}\n\nrev=\"$1\"\nif [ -z \"$rev\" ]; then\n    echo \"Usage: $0 <version>\"\n    exit 1\nfi\n\n\nmkdir \"$rev\"\nd=$(realpath \"$rev\")\n\nwrk=$(mktemp -d)\ntrap \"{ rm -rf \\\"$wrk\\\"; }\" EXIT\ncd \"$wrk\"\n\ngo mod init foo\nGO111MODULE=on go get -d honnef.co/go/tools/cmd/staticcheck@\"$rev\"\n\n\nSYSTEMS=(windows linux freebsd)\nARCHS=(amd64 386)\nfor os in ${SYSTEMS[@]}; do\n    for arch in ${ARCHS[@]}; do\n        build \"$os\" \"$arch\"\n    done\ndone\n\nbuild \"darwin\" \"amd64\"\nbuild \"darwin\" \"arm64\"\n\nfor arch in armv5l armv6l armv7l arm64; do\n    build \"linux\" \"$arch\"\ndone\n\n(\n    cd \"$d\"\n    sha256sum -c --strict *.sha256\n)\n"
  },
  {
    "path": "doc/articles/customizing_staticcheck.html",
    "content": "- how to customize staticcheck\n- tools serve humans\n- tools should assist workflows\n- don't let tools bully you\n\n- exit status\n- which checks run\n- ignoring findings\n- output format\n- go version\n- tests\n"
  },
  {
    "path": "doc/run.html",
    "content": "<h2>Running Staticcheck</h2>\n\n<h3>Checking packages</h3>\n\n<p>\n  The <code>staticcheck</code> command works much like <code>go build</code> or <code>go vet</code> do.\n  It supports all of the same package patterns.\n  For example, <code>staticcheck .</code> will check the current package, and <code>staticcheck ./...</code> will check all packages.\n  For more details on specifying packages to check, see <code>go help packages</code>\n</p>\n\n<h3>Explaining checks</h3>\n\n<p>\n  You can use <code>staticcheck -explain &lt;check&gt;</code> to get a helpful description of a check.\n</p>\n\n<p>\n  Every diagnostic that <code>staticcheck</code> reports is annotated with the identifier of the specific check that found the issue.\n  For example, in\n</p>\n\n<pre><code>foo.go:1248:4: unnecessary use of fmt.Sprintf (S1039)</code></pre>\n\n<p>\n  the check's identifier is <code>S1039</code>.\n  Running <code>staticcheck -explain S1039</code> will output the following:\n</p>\n\n<pre><code>Unnecessary use of fmt.Sprint\n\nCalling fmt.Sprint with a single string argument is unnecessary and identical to using the string directly.\n\nAvailable since\n    2020.1\n\nOnline documentation\n    https://staticcheck.dev/docs/checks#S1039</code></pre>\n\n<p>\n  The output includes a one-line summary,\n  one or more paragraphs of helpful text,\n  the first version of Staticcheck that the check appeared in,\n  and a link to online documentation, which contains the same information as the output of <code>staticcheck -explain</code>.\n</p>\n"
  },
  {
    "path": "generate.go",
    "content": "//go:build ignore\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"go/format\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"text/template\"\n)\n\nvar tmpl = `\n{{define \"analyzers\"}}\n// Code generated by generate.go. DO NOT EDIT.\n\npackage {{.dir}}\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n{{- range $check := .checks}}\n\t\"honnef.co/go/tools/{{$.dir}}/{{$check}}\"\n{{- end}}\n)\n\nvar Analyzers = []*lint.Analyzer{\n{{- range $check := .checks}}\n\t{{$check}}.SCAnalyzer,\n{{- end}}\n}\n{{end}}\n\n{{define \"tests\"}}\n// Code generated by generate.go. DO NOT EDIT.\n\npackage {{.check}}\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n{{end}}\n`\n\nfunc main() {\n\tlog.SetFlags(0)\n\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\tlog.Fatalln(\"couldn't determine current directory:\", err)\n\t}\n\n\tdir = filepath.Base(dir)\n\n\tvar t template.Template\n\tif _, err = t.Parse(tmpl); err != nil {\n\t\tlog.Fatalln(\"couldn't parse templates:\", err)\n\t}\n\n\tdirs, err := filepath.Glob(\"*\")\n\tif err != nil {\n\t\tlog.Fatalln(\"couldn't enumerate checks:\", err)\n\t}\n\n\tcheckRe := regexp.MustCompile(`^[a-z]+\\d{4}$`)\n\tout := dirs[:0]\n\tfor _, dir := range dirs {\n\t\tif checkRe.MatchString(dir) {\n\t\t\tout = append(out, dir)\n\t\t}\n\t}\n\tdirs = out\n\n\tbuf := bytes.NewBuffer(nil)\n\n\tif err := t.ExecuteTemplate(buf, \"analyzers\", map[string]any{\"checks\": dirs, \"dir\": dir}); err != nil {\n\t\tlog.Fatalln(\"couldn't generate analysis.go:\", err)\n\t}\n\n\tb, err := format.Source(buf.Bytes())\n\tif err != nil {\n\t\tlog.Fatalln(\"couldn't gofmt analysis.go:\", err)\n\t}\n\tif err := os.WriteFile(\"analysis.go\", b, 0666); err != nil {\n\t\tlog.Fatalln(\"couldn't write analysis.go:\", err)\n\t}\n\n\tfor _, dir := range dirs {\n\t\tbuf.Reset()\n\t\tdst := filepath.Join(dir, dir+\"_test.go\")\n\t\tif err := t.ExecuteTemplate(buf, \"tests\", map[string]any{\"check\": dir}); err != nil {\n\t\t\tlog.Fatalf(\"couldn't generate %s: %s\", dst, err)\n\t\t}\n\n\t\tb, err := format.Source(buf.Bytes())\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"couldn't gofmt %s: %s\", dst, err)\n\t\t}\n\t\tif err := os.WriteFile(dst, b, 0666); err != nil {\n\t\t\tlog.Fatalf(\"couldn't write %s: %s\", dst, err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "go/ast/astutil/upstream.go",
    "content": "package astutil\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\t_ \"unsafe\"\n\n\t\"golang.org/x/tools/go/ast/astutil\"\n)\n\ntype Cursor = astutil.Cursor\ntype ApplyFunc = astutil.ApplyFunc\n\nfunc Apply(root ast.Node, pre, post ApplyFunc) (result ast.Node) {\n\treturn astutil.Apply(root, pre, post)\n}\n\nfunc PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Node, exact bool) {\n\treturn astutil.PathEnclosingInterval(root, start, end)\n}\n"
  },
  {
    "path": "go/ast/astutil/util.go",
    "content": "package astutil\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"golang.org/x/tools/go/ast/astutil\"\n)\n\nfunc IsIdent(expr ast.Expr, ident string) bool {\n\tid, ok := expr.(*ast.Ident)\n\treturn ok && id.Name == ident\n}\n\n// isBlank returns whether id is the blank identifier \"_\".\n// If id == nil, the answer is false.\nfunc IsBlank(id ast.Expr) bool {\n\tident, _ := id.(*ast.Ident)\n\treturn ident != nil && ident.Name == \"_\"\n}\n\n// Deprecated: use code.IsIntegerLiteral instead.\nfunc IsIntLiteral(expr ast.Expr, literal string) bool {\n\tlit, ok := expr.(*ast.BasicLit)\n\treturn ok && lit.Kind == token.INT && lit.Value == literal\n}\n\n// Deprecated: use IsIntLiteral instead\nfunc IsZero(expr ast.Expr) bool {\n\treturn IsIntLiteral(expr, \"0\")\n}\n\nfunc Preamble(f *ast.File) string {\n\tcutoff := f.Package\n\tif f.Doc != nil {\n\t\tcutoff = f.Doc.Pos()\n\t}\n\tvar out []string\n\tfor _, cmt := range f.Comments {\n\t\tif cmt.Pos() >= cutoff {\n\t\t\tbreak\n\t\t}\n\t\tout = append(out, cmt.Text())\n\t}\n\treturn strings.Join(out, \"\\n\")\n}\n\nfunc GroupSpecs(fset *token.FileSet, specs []ast.Spec) [][]ast.Spec {\n\tif len(specs) == 0 {\n\t\treturn nil\n\t}\n\tgroups := make([][]ast.Spec, 1)\n\tgroups[0] = append(groups[0], specs[0])\n\n\tfor _, spec := range specs[1:] {\n\t\tg := groups[len(groups)-1]\n\t\tif fset.PositionFor(spec.Pos(), false).Line-1 !=\n\t\t\tfset.PositionFor(g[len(g)-1].End(), false).Line {\n\n\t\t\tgroups = append(groups, nil)\n\t\t}\n\n\t\tgroups[len(groups)-1] = append(groups[len(groups)-1], spec)\n\t}\n\n\treturn groups\n}\n\n// Unparen returns e with any enclosing parentheses stripped.\nfunc Unparen(e ast.Expr) ast.Expr {\n\tfor {\n\t\tp, ok := e.(*ast.ParenExpr)\n\t\tif !ok {\n\t\t\treturn e\n\t\t}\n\t\te = p.X\n\t}\n}\n\n// CopyExpr creates a deep copy of an expression.\n// It doesn't support copying FuncLits and returns ok == false when encountering one.\nfunc CopyExpr(node ast.Expr) (ast.Expr, bool) {\n\tswitch node := node.(type) {\n\tcase *ast.BasicLit:\n\t\tcp := *node\n\t\treturn &cp, true\n\tcase *ast.BinaryExpr:\n\t\tcp := *node\n\t\tvar ok1, ok2 bool\n\t\tcp.X, ok1 = CopyExpr(cp.X)\n\t\tcp.Y, ok2 = CopyExpr(cp.Y)\n\t\treturn &cp, ok1 && ok2\n\tcase *ast.CallExpr:\n\t\tvar ok bool\n\t\tcp := *node\n\t\tcp.Fun, ok = CopyExpr(cp.Fun)\n\t\tif !ok {\n\t\t\treturn nil, false\n\t\t}\n\t\tcp.Args = make([]ast.Expr, len(node.Args))\n\t\tfor i, v := range node.Args {\n\t\t\tcp.Args[i], ok = CopyExpr(v)\n\t\t\tif !ok {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t}\n\t\treturn &cp, true\n\tcase *ast.CompositeLit:\n\t\tvar ok bool\n\t\tcp := *node\n\t\tcp.Type, ok = CopyExpr(cp.Type)\n\t\tif !ok {\n\t\t\treturn nil, false\n\t\t}\n\t\tcp.Elts = make([]ast.Expr, len(node.Elts))\n\t\tfor i, v := range node.Elts {\n\t\t\tcp.Elts[i], ok = CopyExpr(v)\n\t\t\tif !ok {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t}\n\t\treturn &cp, true\n\tcase *ast.Ident:\n\t\tcp := *node\n\t\treturn &cp, true\n\tcase *ast.IndexExpr:\n\t\tvar ok1, ok2 bool\n\t\tcp := *node\n\t\tcp.X, ok1 = CopyExpr(cp.X)\n\t\tcp.Index, ok2 = CopyExpr(cp.Index)\n\t\treturn &cp, ok1 && ok2\n\tcase *ast.IndexListExpr:\n\t\tvar ok bool\n\t\tcp := *node\n\t\tcp.X, ok = CopyExpr(cp.X)\n\t\tif !ok {\n\t\t\treturn nil, false\n\t\t}\n\t\tfor i, v := range node.Indices {\n\t\t\tcp.Indices[i], ok = CopyExpr(v)\n\t\t\tif !ok {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t}\n\t\treturn &cp, true\n\tcase *ast.KeyValueExpr:\n\t\tvar ok1, ok2 bool\n\t\tcp := *node\n\t\tcp.Key, ok1 = CopyExpr(cp.Key)\n\t\tcp.Value, ok2 = CopyExpr(cp.Value)\n\t\treturn &cp, ok1 && ok2\n\tcase *ast.ParenExpr:\n\t\tvar ok bool\n\t\tcp := *node\n\t\tcp.X, ok = CopyExpr(cp.X)\n\t\treturn &cp, ok\n\tcase *ast.SelectorExpr:\n\t\tvar ok bool\n\t\tcp := *node\n\t\tcp.X, ok = CopyExpr(cp.X)\n\t\tif !ok {\n\t\t\treturn nil, false\n\t\t}\n\t\tsel, ok := CopyExpr(cp.Sel)\n\t\tif !ok {\n\t\t\t// this is impossible\n\t\t\treturn nil, false\n\t\t}\n\t\tcp.Sel = sel.(*ast.Ident)\n\t\treturn &cp, true\n\tcase *ast.SliceExpr:\n\t\tvar ok1, ok2, ok3, ok4 bool\n\t\tcp := *node\n\t\tcp.X, ok1 = CopyExpr(cp.X)\n\t\tcp.Low, ok2 = CopyExpr(cp.Low)\n\t\tcp.High, ok3 = CopyExpr(cp.High)\n\t\tcp.Max, ok4 = CopyExpr(cp.Max)\n\t\treturn &cp, ok1 && ok2 && ok3 && ok4\n\tcase *ast.StarExpr:\n\t\tvar ok bool\n\t\tcp := *node\n\t\tcp.X, ok = CopyExpr(cp.X)\n\t\treturn &cp, ok\n\tcase *ast.TypeAssertExpr:\n\t\tvar ok1, ok2 bool\n\t\tcp := *node\n\t\tcp.X, ok1 = CopyExpr(cp.X)\n\t\tcp.Type, ok2 = CopyExpr(cp.Type)\n\t\treturn &cp, ok1 && ok2\n\tcase *ast.UnaryExpr:\n\t\tvar ok bool\n\t\tcp := *node\n\t\tcp.X, ok = CopyExpr(cp.X)\n\t\treturn &cp, ok\n\tcase *ast.MapType:\n\t\tvar ok1, ok2 bool\n\t\tcp := *node\n\t\tcp.Key, ok1 = CopyExpr(cp.Key)\n\t\tcp.Value, ok2 = CopyExpr(cp.Value)\n\t\treturn &cp, ok1 && ok2\n\tcase *ast.ArrayType:\n\t\tvar ok1, ok2 bool\n\t\tcp := *node\n\t\tcp.Len, ok1 = CopyExpr(cp.Len)\n\t\tcp.Elt, ok2 = CopyExpr(cp.Elt)\n\t\treturn &cp, ok1 && ok2\n\tcase *ast.Ellipsis:\n\t\tvar ok bool\n\t\tcp := *node\n\t\tcp.Elt, ok = CopyExpr(cp.Elt)\n\t\treturn &cp, ok\n\tcase *ast.InterfaceType:\n\t\tcp := *node\n\t\treturn &cp, true\n\tcase *ast.StructType:\n\t\tcp := *node\n\t\treturn &cp, true\n\tcase *ast.FuncLit, *ast.FuncType:\n\t\t// TODO(dh): implement copying of function literals and types.\n\t\treturn nil, false\n\tcase *ast.ChanType:\n\t\tvar ok bool\n\t\tcp := *node\n\t\tcp.Value, ok = CopyExpr(cp.Value)\n\t\treturn &cp, ok\n\tcase nil:\n\t\treturn nil, true\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unreachable: %T\", node))\n\t}\n}\n\nfunc Equal(a, b ast.Node) bool {\n\tif a == b {\n\t\treturn true\n\t}\n\tif a == nil || b == nil {\n\t\treturn false\n\t}\n\tif reflect.TypeOf(a) != reflect.TypeOf(b) {\n\t\treturn false\n\t}\n\n\tswitch a := a.(type) {\n\tcase *ast.BasicLit:\n\t\tb := b.(*ast.BasicLit)\n\t\treturn a.Kind == b.Kind && a.Value == b.Value\n\tcase *ast.BinaryExpr:\n\t\tb := b.(*ast.BinaryExpr)\n\t\treturn Equal(a.X, b.X) && a.Op == b.Op && Equal(a.Y, b.Y)\n\tcase *ast.CallExpr:\n\t\tb := b.(*ast.CallExpr)\n\t\tif len(a.Args) != len(b.Args) {\n\t\t\treturn false\n\t\t}\n\t\tfor i, arg := range a.Args {\n\t\t\tif !Equal(arg, b.Args[i]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn Equal(a.Fun, b.Fun) &&\n\t\t\t(a.Ellipsis == token.NoPos && b.Ellipsis == token.NoPos || a.Ellipsis != token.NoPos && b.Ellipsis != token.NoPos)\n\tcase *ast.CompositeLit:\n\t\tb := b.(*ast.CompositeLit)\n\t\tif len(a.Elts) != len(b.Elts) {\n\t\t\treturn false\n\t\t}\n\t\tfor i, elt := range b.Elts {\n\t\t\tif !Equal(elt, b.Elts[i]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn Equal(a.Type, b.Type) && a.Incomplete == b.Incomplete\n\tcase *ast.Ident:\n\t\tb := b.(*ast.Ident)\n\t\treturn a.Name == b.Name\n\tcase *ast.IndexExpr:\n\t\tb := b.(*ast.IndexExpr)\n\t\treturn Equal(a.X, b.X) && Equal(a.Index, b.Index)\n\tcase *ast.IndexListExpr:\n\t\tb := b.(*ast.IndexListExpr)\n\t\tif len(a.Indices) != len(b.Indices) {\n\t\t\treturn false\n\t\t}\n\t\tfor i, v := range a.Indices {\n\t\t\tif !Equal(v, b.Indices[i]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn Equal(a.X, b.X)\n\tcase *ast.KeyValueExpr:\n\t\tb := b.(*ast.KeyValueExpr)\n\t\treturn Equal(a.Key, b.Key) && Equal(a.Value, b.Value)\n\tcase *ast.ParenExpr:\n\t\tb := b.(*ast.ParenExpr)\n\t\treturn Equal(a.X, b.X)\n\tcase *ast.SelectorExpr:\n\t\tb := b.(*ast.SelectorExpr)\n\t\treturn Equal(a.X, b.X) && Equal(a.Sel, b.Sel)\n\tcase *ast.SliceExpr:\n\t\tb := b.(*ast.SliceExpr)\n\t\treturn Equal(a.X, b.X) && Equal(a.Low, b.Low) && Equal(a.High, b.High) && Equal(a.Max, b.Max) && a.Slice3 == b.Slice3\n\tcase *ast.StarExpr:\n\t\tb := b.(*ast.StarExpr)\n\t\treturn Equal(a.X, b.X)\n\tcase *ast.TypeAssertExpr:\n\t\tb := b.(*ast.TypeAssertExpr)\n\t\treturn Equal(a.X, b.X) && Equal(a.Type, b.Type)\n\tcase *ast.UnaryExpr:\n\t\tb := b.(*ast.UnaryExpr)\n\t\treturn a.Op == b.Op && Equal(a.X, b.X)\n\tcase *ast.MapType:\n\t\tb := b.(*ast.MapType)\n\t\treturn Equal(a.Key, b.Key) && Equal(a.Value, b.Value)\n\tcase *ast.ArrayType:\n\t\tb := b.(*ast.ArrayType)\n\t\treturn Equal(a.Len, b.Len) && Equal(a.Elt, b.Elt)\n\tcase *ast.Ellipsis:\n\t\tb := b.(*ast.Ellipsis)\n\t\treturn Equal(a.Elt, b.Elt)\n\tcase *ast.InterfaceType:\n\t\tb := b.(*ast.InterfaceType)\n\t\treturn a.Incomplete == b.Incomplete && Equal(a.Methods, b.Methods)\n\tcase *ast.StructType:\n\t\tb := b.(*ast.StructType)\n\t\treturn a.Incomplete == b.Incomplete && Equal(a.Fields, b.Fields)\n\tcase *ast.FuncLit:\n\t\t// TODO(dh): support function literals\n\t\treturn false\n\tcase *ast.ChanType:\n\t\tb := b.(*ast.ChanType)\n\t\treturn a.Dir == b.Dir && (a.Arrow == token.NoPos && b.Arrow == token.NoPos || a.Arrow != token.NoPos && b.Arrow != token.NoPos)\n\tcase *ast.FieldList:\n\t\tb := b.(*ast.FieldList)\n\t\tif len(a.List) != len(b.List) {\n\t\t\treturn false\n\t\t}\n\t\tfor i, fieldA := range a.List {\n\t\t\tif !Equal(fieldA, b.List[i]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\tcase *ast.Field:\n\t\tb := b.(*ast.Field)\n\t\tif len(a.Names) != len(b.Names) {\n\t\t\treturn false\n\t\t}\n\t\tfor j, name := range a.Names {\n\t\t\tif !Equal(name, b.Names[j]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\tif !Equal(a.Type, b.Type) || !Equal(a.Tag, b.Tag) {\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unreachable: %T\", a))\n\t}\n}\n\nfunc NegateDeMorgan(expr ast.Expr, recursive bool) ast.Expr {\n\tswitch expr := expr.(type) {\n\tcase *ast.BinaryExpr:\n\t\tvar out ast.BinaryExpr\n\t\tswitch expr.Op {\n\t\tcase token.EQL:\n\t\t\tout.X = expr.X\n\t\t\tout.Op = token.NEQ\n\t\t\tout.Y = expr.Y\n\t\tcase token.LSS:\n\t\t\tout.X = expr.X\n\t\t\tout.Op = token.GEQ\n\t\t\tout.Y = expr.Y\n\t\tcase token.GTR:\n\t\t\tout.X = expr.X\n\t\t\tout.Op = token.LEQ\n\t\t\tout.Y = expr.Y\n\t\tcase token.NEQ:\n\t\t\tout.X = expr.X\n\t\t\tout.Op = token.EQL\n\t\t\tout.Y = expr.Y\n\t\tcase token.LEQ:\n\t\t\tout.X = expr.X\n\t\t\tout.Op = token.GTR\n\t\t\tout.Y = expr.Y\n\t\tcase token.GEQ:\n\t\t\tout.X = expr.X\n\t\t\tout.Op = token.LSS\n\t\t\tout.Y = expr.Y\n\n\t\tcase token.LAND:\n\t\t\tout.X = NegateDeMorgan(expr.X, recursive)\n\t\t\tout.Op = token.LOR\n\t\t\tout.Y = NegateDeMorgan(expr.Y, recursive)\n\t\tcase token.LOR:\n\t\t\tout.X = NegateDeMorgan(expr.X, recursive)\n\t\t\tout.Op = token.LAND\n\t\t\tout.Y = NegateDeMorgan(expr.Y, recursive)\n\t\t}\n\t\treturn &out\n\n\tcase *ast.ParenExpr:\n\t\tif recursive {\n\t\t\treturn &ast.ParenExpr{\n\t\t\t\tX: NegateDeMorgan(expr.X, recursive),\n\t\t\t}\n\t\t} else {\n\t\t\treturn &ast.UnaryExpr{\n\t\t\t\tOp: token.NOT,\n\t\t\t\tX:  expr,\n\t\t\t}\n\t\t}\n\n\tcase *ast.UnaryExpr:\n\t\tif expr.Op == token.NOT {\n\t\t\treturn expr.X\n\t\t} else {\n\t\t\treturn &ast.UnaryExpr{\n\t\t\t\tOp: token.NOT,\n\t\t\t\tX:  expr,\n\t\t\t}\n\t\t}\n\n\tdefault:\n\t\treturn &ast.UnaryExpr{\n\t\t\tOp: token.NOT,\n\t\t\tX:  expr,\n\t\t}\n\t}\n}\n\nfunc SimplifyParentheses(node ast.Expr) ast.Expr {\n\tvar changed bool\n\t// XXX accept list of ops to operate on\n\t// XXX copy AST node, don't modify in place\n\tpost := func(c *astutil.Cursor) bool {\n\t\tout := c.Node()\n\t\tif paren, ok := c.Node().(*ast.ParenExpr); ok {\n\t\t\tout = paren.X\n\t\t}\n\n\t\tif binop, ok := out.(*ast.BinaryExpr); ok {\n\t\t\tif right, ok := binop.Y.(*ast.BinaryExpr); ok && binop.Op == right.Op {\n\t\t\t\t// XXX also check that Op is associative\n\n\t\t\t\troot := binop\n\t\t\t\tpivot := root.Y.(*ast.BinaryExpr)\n\t\t\t\troot.Y = pivot.X\n\t\t\t\tpivot.X = root\n\t\t\t\troot = pivot\n\t\t\t\tout = root\n\t\t\t}\n\t\t}\n\n\t\tif out != c.Node() {\n\t\t\tchanged = true\n\t\t\tc.Replace(out)\n\t\t}\n\t\treturn true\n\t}\n\n\tfor changed = true; changed; {\n\t\tchanged = false\n\t\tnode = astutil.Apply(node, nil, post).(ast.Expr)\n\t}\n\n\treturn node\n}\n"
  },
  {
    "path": "go/buildid/UPSTREAM",
    "content": "This package extracts buildid.go and note.go from cmd/internal/buildid/.\n\nWe have modified it to remove support for AIX big archive files, to cut down on our dependencies.\n\nThe last upstream commit we've looked at was: d3ddc4854429185e6e06ca1f7628bb790404abb5\n"
  },
  {
    "path": "go/buildid/buildid.go",
    "content": "// Copyright 2017 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage buildid\n\nimport (\n\t\"bytes\"\n\t\"debug/elf\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nvar errBuildIDMalformed = fmt.Errorf(\"malformed object file\")\n\nvar (\n\tbangArch = []byte(\"!<arch>\")\n\tpkgdef   = []byte(\"__.PKGDEF\")\n\tgoobject = []byte(\"go object \")\n\tbuildid  = []byte(\"build id \")\n)\n\n// ReadFile reads the build ID from an archive or executable file.\nfunc ReadFile(name string) (id string, err error) {\n\tf, err := os.Open(name)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer f.Close()\n\n\tbuf := make([]byte, 8)\n\tif _, err := f.ReadAt(buf, 0); err != nil {\n\t\treturn \"\", err\n\t}\n\tif string(buf) != \"!<arch>\\n\" {\n\t\tif string(buf) == \"<bigaf>\\n\" {\n\t\t\treturn \"\", errors.New(\"unsupported\")\n\t\t}\n\t\treturn readBinary(name, f)\n\t}\n\n\t// Read just enough of the target to fetch the build ID.\n\t// The archive is expected to look like:\n\t//\n\t//\t!<arch>\n\t//\t__.PKGDEF       0           0     0     644     7955      `\n\t//\tgo object darwin amd64 devel X:none\n\t//\tbuild id \"b41e5c45250e25c9fd5e9f9a1de7857ea0d41224\"\n\t//\n\t// The variable-sized strings are GOOS, GOARCH, and the experiment list (X:none).\n\t// Reading the first 1024 bytes should be plenty.\n\tdata := make([]byte, 1024)\n\tn, err := io.ReadFull(f, data)\n\tif err != nil && n == 0 {\n\t\treturn \"\", err\n\t}\n\n\ttryGccgo := func() (string, error) {\n\t\treturn readGccgoArchive(name, f)\n\t}\n\n\t// Archive header.\n\tfor i := 0; ; i++ { // returns during i==3\n\t\tj := bytes.IndexByte(data, '\\n')\n\t\tif j < 0 {\n\t\t\treturn tryGccgo()\n\t\t}\n\t\tline := data[:j]\n\t\tdata = data[j+1:]\n\t\tswitch i {\n\t\tcase 0:\n\t\t\tif !bytes.Equal(line, bangArch) {\n\t\t\t\treturn tryGccgo()\n\t\t\t}\n\t\tcase 1:\n\t\t\tif !bytes.HasPrefix(line, pkgdef) {\n\t\t\t\treturn tryGccgo()\n\t\t\t}\n\t\tcase 2:\n\t\t\tif !bytes.HasPrefix(line, goobject) {\n\t\t\t\treturn tryGccgo()\n\t\t\t}\n\t\tcase 3:\n\t\t\tif !bytes.HasPrefix(line, buildid) {\n\t\t\t\t// Found the object header, just doesn't have a build id line.\n\t\t\t\t// Treat as successful, with empty build id.\n\t\t\t\treturn \"\", nil\n\t\t\t}\n\t\t\tid, err := strconv.Unquote(string(line[len(buildid):]))\n\t\t\tif err != nil {\n\t\t\t\treturn tryGccgo()\n\t\t\t}\n\t\t\treturn id, nil\n\t\t}\n\t}\n}\n\n// readGccgoArchive tries to parse the archive as a standard Unix\n// archive file, and fetch the build ID from the _buildid.o entry.\n// The _buildid.o entry is written by (*Builder).gccgoBuildIDELFFile\n// in cmd/go/internal/work/exec.go.\nfunc readGccgoArchive(name string, f *os.File) (string, error) {\n\tbad := func() (string, error) {\n\t\treturn \"\", &os.PathError{Op: \"parse\", Path: name, Err: errBuildIDMalformed}\n\t}\n\n\toff := int64(8)\n\tfor {\n\t\tif _, err := f.Seek(off, io.SeekStart); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\t// TODO(iant): Make a debug/ar package, and use it\n\t\t// here and in cmd/link.\n\t\tvar hdr [60]byte\n\t\tif _, err := io.ReadFull(f, hdr[:]); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\t// No more entries, no build ID.\n\t\t\t\treturn \"\", nil\n\t\t\t}\n\t\t\treturn \"\", err\n\t\t}\n\t\toff += 60\n\n\t\tsizeStr := strings.TrimSpace(string(hdr[48:58]))\n\t\tsize, err := strconv.ParseInt(sizeStr, 0, 64)\n\t\tif err != nil {\n\t\t\treturn bad()\n\t\t}\n\n\t\tname := strings.TrimSpace(string(hdr[:16]))\n\t\tif name == \"_buildid.o/\" {\n\t\t\tsr := io.NewSectionReader(f, off, size)\n\t\t\te, err := elf.NewFile(sr)\n\t\t\tif err != nil {\n\t\t\t\treturn bad()\n\t\t\t}\n\t\t\ts := e.Section(\".go.buildid\")\n\t\t\tif s == nil {\n\t\t\t\treturn bad()\n\t\t\t}\n\t\t\tdata, err := s.Data()\n\t\t\tif err != nil {\n\t\t\t\treturn bad()\n\t\t\t}\n\t\t\treturn string(data), nil\n\t\t}\n\n\t\toff += size\n\t\tif off&1 != 0 {\n\t\t\toff++\n\t\t}\n\t}\n}\n\nvar (\n\tgoBuildPrefix = []byte(\"\\xff Go build ID: \\\"\")\n\tgoBuildEnd    = []byte(\"\\\"\\n \\xff\")\n\n\telfPrefix = []byte(\"\\x7fELF\")\n\n\tmachoPrefixes = [][]byte{\n\t\t{0xfe, 0xed, 0xfa, 0xce},\n\t\t{0xfe, 0xed, 0xfa, 0xcf},\n\t\t{0xce, 0xfa, 0xed, 0xfe},\n\t\t{0xcf, 0xfa, 0xed, 0xfe},\n\t}\n)\n\nvar readSize = 32 * 1024 // changed for testing\n\n// readBinary reads the build ID from a binary.\n//\n// ELF binaries store the build ID in a proper PT_NOTE section.\n//\n// Other binary formats are not so flexible. For those, the linker\n// stores the build ID as non-instruction bytes at the very beginning\n// of the text segment, which should appear near the beginning\n// of the file. This is clumsy but fairly portable. Custom locations\n// can be added for other binary types as needed, like we did for ELF.\nfunc readBinary(name string, f *os.File) (id string, err error) {\n\t// Read the first 32 kB of the binary file.\n\t// That should be enough to find the build ID.\n\t// In ELF files, the build ID is in the leading headers,\n\t// which are typically less than 4 kB, not to mention 32 kB.\n\t// In Mach-O files, there's no limit, so we have to parse the file.\n\t// On other systems, we're trying to read enough that\n\t// we get the beginning of the text segment in the read.\n\t// The offset where the text segment begins in a hello\n\t// world compiled for each different object format today:\n\t//\n\t//\tPlan 9: 0x20\n\t//\tWindows: 0x600\n\t//\n\tdata := make([]byte, readSize)\n\t_, err = io.ReadFull(f, data)\n\tif err == io.ErrUnexpectedEOF {\n\t\terr = nil\n\t}\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif bytes.HasPrefix(data, elfPrefix) {\n\t\treturn readELF(name, f, data)\n\t}\n\tfor _, m := range machoPrefixes {\n\t\tif bytes.HasPrefix(data, m) {\n\t\t\treturn readMacho(name, f, data)\n\t\t}\n\t}\n\treturn readRaw(name, data)\n}\n\n// readRaw finds the raw build ID stored in text segment data.\nfunc readRaw(name string, data []byte) (id string, err error) {\n\ti := bytes.Index(data, goBuildPrefix)\n\tif i < 0 {\n\t\t// Missing. Treat as successful but build ID empty.\n\t\treturn \"\", nil\n\t}\n\n\tj := bytes.Index(data[i+len(goBuildPrefix):], goBuildEnd)\n\tif j < 0 {\n\t\treturn \"\", &os.PathError{Op: \"parse\", Path: name, Err: errBuildIDMalformed}\n\t}\n\n\tquoted := data[i+len(goBuildPrefix)-1 : i+len(goBuildPrefix)+j+1]\n\tid, err = strconv.Unquote(string(quoted))\n\tif err != nil {\n\t\treturn \"\", &os.PathError{Op: \"parse\", Path: name, Err: errBuildIDMalformed}\n\t}\n\treturn id, nil\n}\n"
  },
  {
    "path": "go/buildid/note.go",
    "content": "// Copyright 2015 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage buildid\n\nimport (\n\t\"bytes\"\n\t\"debug/elf\"\n\t\"debug/macho\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc readAligned4(r io.Reader, sz int32) ([]byte, error) {\n\tfull := (sz + 3) &^ 3\n\tdata := make([]byte, full)\n\t_, err := io.ReadFull(r, data)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata = data[:sz]\n\treturn data, nil\n}\n\nfunc ReadELFNote(filename, name string, typ int32) ([]byte, error) {\n\tf, err := elf.Open(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\tfor _, sect := range f.Sections {\n\t\tif sect.Type != elf.SHT_NOTE {\n\t\t\tcontinue\n\t\t}\n\t\tr := sect.Open()\n\t\tfor {\n\t\t\tvar namesize, descsize, noteType int32\n\t\t\terr = binary.Read(r, f.ByteOrder, &namesize)\n\t\t\tif err != nil {\n\t\t\t\tif err == io.EOF {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\treturn nil, fmt.Errorf(\"read namesize failed: %v\", err)\n\t\t\t}\n\t\t\terr = binary.Read(r, f.ByteOrder, &descsize)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"read descsize failed: %v\", err)\n\t\t\t}\n\t\t\terr = binary.Read(r, f.ByteOrder, &noteType)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"read type failed: %v\", err)\n\t\t\t}\n\t\t\tnoteName, err := readAligned4(r, namesize)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"read name failed: %v\", err)\n\t\t\t}\n\t\t\tdesc, err := readAligned4(r, descsize)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"read desc failed: %v\", err)\n\t\t\t}\n\t\t\tif name == string(noteName) && typ == noteType {\n\t\t\t\treturn desc, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n\nvar elfGoNote = []byte(\"Go\\x00\\x00\")\nvar elfGNUNote = []byte(\"GNU\\x00\")\n\n// The Go build ID is stored in a note described by an ELF PT_NOTE prog\n// header. The caller has already opened filename, to get f, and read\n// at least 4 kB out, in data.\nfunc readELF(name string, f *os.File, data []byte) (buildid string, err error) {\n\t// Assume the note content is in the data, already read.\n\t// Rewrite the ELF header to set shnum to 0, so that we can pass\n\t// the data to elf.NewFile and it will decode the Prog list but not\n\t// try to read the section headers and the string table from disk.\n\t// That's a waste of I/O when all we care about is the Prog list\n\t// and the one ELF note.\n\tswitch elf.Class(data[elf.EI_CLASS]) {\n\tcase elf.ELFCLASS32:\n\t\tdata[48] = 0\n\t\tdata[49] = 0\n\tcase elf.ELFCLASS64:\n\t\tdata[60] = 0\n\t\tdata[61] = 0\n\t}\n\n\tconst elfGoBuildIDTag = 4\n\tconst gnuBuildIDTag = 3\n\n\tef, err := elf.NewFile(bytes.NewReader(data))\n\tif err != nil {\n\t\treturn \"\", &os.PathError{Path: name, Op: \"parse\", Err: err}\n\t}\n\tvar gnu string\n\tfor _, p := range ef.Progs {\n\t\tif p.Type != elf.PT_NOTE || p.Filesz < 16 {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar note []byte\n\t\tif p.Off+p.Filesz < uint64(len(data)) {\n\t\t\tnote = data[p.Off : p.Off+p.Filesz]\n\t\t} else {\n\t\t\t// For some linkers, such as the Solaris linker,\n\t\t\t// the buildid may not be found in data (which\n\t\t\t// likely contains the first 16kB of the file)\n\t\t\t// or even the first few megabytes of the file\n\t\t\t// due to differences in note segment placement;\n\t\t\t// in that case, extract the note data manually.\n\t\t\t_, err = f.Seek(int64(p.Off), io.SeekStart)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\n\t\t\tnote = make([]byte, p.Filesz)\n\t\t\t_, err = io.ReadFull(f, note)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t}\n\n\t\tfilesz := p.Filesz\n\t\toff := p.Off\n\t\tfor filesz >= 16 {\n\t\t\tnameSize := ef.ByteOrder.Uint32(note)\n\t\t\tvalSize := ef.ByteOrder.Uint32(note[4:])\n\t\t\ttag := ef.ByteOrder.Uint32(note[8:])\n\t\t\tnname := note[12:16]\n\t\t\tif nameSize == 4 && 16+valSize <= uint32(len(note)) && tag == elfGoBuildIDTag && bytes.Equal(nname, elfGoNote) {\n\t\t\t\treturn string(note[16 : 16+valSize]), nil\n\t\t\t}\n\n\t\t\tif nameSize == 4 && 16+valSize <= uint32(len(note)) && tag == gnuBuildIDTag && bytes.Equal(nname, elfGNUNote) {\n\t\t\t\tgnu = string(note[16 : 16+valSize])\n\t\t\t}\n\n\t\t\tnameSize = (nameSize + 3) &^ 3\n\t\t\tvalSize = (valSize + 3) &^ 3\n\t\t\tnotesz := uint64(12 + nameSize + valSize)\n\t\t\tif filesz <= notesz {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\toff += notesz\n\t\t\talign := p.Align\n\t\t\talignedOff := (off + align - 1) &^ (align - 1)\n\t\t\tnotesz += alignedOff - off\n\t\t\toff = alignedOff\n\t\t\tfilesz -= notesz\n\t\t\tnote = note[notesz:]\n\t\t}\n\t}\n\n\t// If we didn't find a Go note, use a GNU note if available.\n\t// This is what gccgo uses.\n\tif gnu != \"\" {\n\t\treturn gnu, nil\n\t}\n\n\t// No note. Treat as successful but build ID empty.\n\treturn \"\", nil\n}\n\n// The Go build ID is stored at the beginning of the Mach-O __text segment.\n// The caller has already opened filename, to get f, and read a few kB out, in data.\n// Sadly, that's not guaranteed to hold the note, because there is an arbitrary amount\n// of other junk placed in the file ahead of the main text.\nfunc readMacho(name string, f *os.File, data []byte) (buildid string, err error) {\n\t// If the data we want has already been read, don't worry about Mach-O parsing.\n\t// This is both an optimization and a hedge against the Mach-O parsing failing\n\t// in the future due to, for example, the name of the __text section changing.\n\tif b, err := readRaw(name, data); b != \"\" && err == nil {\n\t\treturn b, err\n\t}\n\n\tmf, err := macho.NewFile(f)\n\tif err != nil {\n\t\treturn \"\", &os.PathError{Path: name, Op: \"parse\", Err: err}\n\t}\n\n\tsect := mf.Section(\"__text\")\n\tif sect == nil {\n\t\t// Every binary has a __text section. Something is wrong.\n\t\treturn \"\", &os.PathError{Path: name, Op: \"parse\", Err: fmt.Errorf(\"cannot find __text section\")}\n\t}\n\n\t// It should be in the first few bytes, but read a lot just in case,\n\t// especially given our past problems on OS X with the build ID moving.\n\t// There shouldn't be much difference between reading 4kB and 32kB:\n\t// the hard part is getting to the data, not transferring it.\n\tn := min(sect.Size, uint64(readSize))\n\tbuf := make([]byte, n)\n\tif _, err := f.ReadAt(buf, int64(sect.Offset)); err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn readRaw(name, buf)\n}\n"
  },
  {
    "path": "go/gcsizes/LICENSE",
    "content": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "go/gcsizes/sizes.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package gcsizes provides a types.Sizes implementation that adheres\n// to the rules used by the gc compiler.\npackage gcsizes\n\nimport (\n\t\"go/build\"\n\t\"go/types\"\n)\n\ntype Sizes struct {\n\tWordSize int64\n\tMaxAlign int64\n}\n\n// ForArch returns a correct Sizes for the given architecture.\nfunc ForArch(arch string) *Sizes {\n\twordSize := int64(8)\n\tmaxAlign := int64(8)\n\tswitch build.Default.GOARCH {\n\tcase \"386\", \"arm\":\n\t\twordSize, maxAlign = 4, 4\n\tcase \"amd64p32\":\n\t\twordSize = 4\n\t}\n\treturn &Sizes{WordSize: wordSize, MaxAlign: maxAlign}\n}\n\nfunc (s *Sizes) Alignof(T types.Type) int64 {\n\tswitch t := T.Underlying().(type) {\n\tcase *types.Array:\n\t\treturn s.Alignof(t.Elem())\n\tcase *types.Struct:\n\t\tmax := int64(1)\n\t\tn := t.NumFields()\n\t\tvar fields []*types.Var\n\t\tfor i := range n {\n\t\t\tfields = append(fields, t.Field(i))\n\t\t}\n\t\tfor _, f := range fields {\n\t\t\tif a := s.Alignof(f.Type()); a > max {\n\t\t\t\tmax = a\n\t\t\t}\n\t\t}\n\t\treturn max\n\t}\n\ta := s.Sizeof(T) // may be 0\n\tif a < 1 {\n\t\treturn 1\n\t}\n\tif a > s.MaxAlign {\n\t\treturn s.MaxAlign\n\t}\n\treturn a\n}\n\nfunc (s *Sizes) Offsetsof(fields []*types.Var) []int64 {\n\toffsets := make([]int64, len(fields))\n\tvar o int64\n\tfor i, f := range fields {\n\t\ta := s.Alignof(f.Type())\n\t\to = align(o, a)\n\t\toffsets[i] = o\n\t\to += s.Sizeof(f.Type())\n\t}\n\treturn offsets\n}\n\nvar basicSizes = [...]byte{\n\ttypes.Bool:       1,\n\ttypes.Int8:       1,\n\ttypes.Int16:      2,\n\ttypes.Int32:      4,\n\ttypes.Int64:      8,\n\ttypes.Uint8:      1,\n\ttypes.Uint16:     2,\n\ttypes.Uint32:     4,\n\ttypes.Uint64:     8,\n\ttypes.Float32:    4,\n\ttypes.Float64:    8,\n\ttypes.Complex64:  8,\n\ttypes.Complex128: 16,\n}\n\nfunc (s *Sizes) Sizeof(T types.Type) int64 {\n\tswitch t := T.Underlying().(type) {\n\tcase *types.Basic:\n\t\tk := t.Kind()\n\t\tif int(k) < len(basicSizes) {\n\t\t\tif s := basicSizes[k]; s > 0 {\n\t\t\t\treturn int64(s)\n\t\t\t}\n\t\t}\n\t\tif k == types.String {\n\t\t\treturn s.WordSize * 2\n\t\t}\n\tcase *types.Array:\n\t\tn := t.Len()\n\t\tif n == 0 {\n\t\t\treturn 0\n\t\t}\n\t\ta := s.Alignof(t.Elem())\n\t\tz := s.Sizeof(t.Elem())\n\t\treturn align(z, a)*(n-1) + z\n\tcase *types.Slice:\n\t\treturn s.WordSize * 3\n\tcase *types.Struct:\n\t\tn := t.NumFields()\n\t\tif n == 0 {\n\t\t\treturn 0\n\t\t}\n\n\t\tvar fields []*types.Var\n\t\tfor i := range n {\n\t\t\tfields = append(fields, t.Field(i))\n\t\t}\n\t\toffsets := s.Offsetsof(fields)\n\t\ta := s.Alignof(T)\n\t\tlsz := s.Sizeof(fields[n-1].Type())\n\t\tif lsz == 0 {\n\t\t\tlsz = 1\n\t\t}\n\t\tz := offsets[n-1] + lsz\n\t\treturn align(z, a)\n\tcase *types.Interface:\n\t\treturn s.WordSize * 2\n\t}\n\treturn s.WordSize // catch-all\n}\n\n// align returns the smallest y >= x such that y % a == 0.\nfunc align(x, a int64) int64 {\n\ty := x + a - 1\n\treturn y - y%a\n}\n"
  },
  {
    "path": "go/ir/LICENSE",
    "content": "Copyright (c) 2009 The Go Authors. All rights reserved.\nCopyright (c) 2016 Dominik Honnef. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "go/ir/UPSTREAM",
    "content": "This package started as a copy of golang.org/x/tools/go/ssa, imported from an unknown commit in 2016.\nIt has since been heavily modified to match our own needs in an IR.\nThe changes are too many to list here, and it is best to consider this package independent of go/ssa.\n\nUpstream changes still get applied when they address bugs in portions of code we have inherited.\n\nThe last upstream commit we've looked at was:\n05409620da166985e94b711ad4103bee40406eee\n\n"
  },
  {
    "path": "go/ir/bench_test.go",
    "content": "package ir_test\n\nimport (\n\t\"testing\"\n\n\t\"golang.org/x/tools/go/packages\"\n\t\"honnef.co/go/tools/go/ir\"\n)\n\nfunc BenchmarkSSA(b *testing.B) {\n\tcfg := &packages.Config{\n\t\tMode:  packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo,\n\t\tTests: false,\n\t}\n\tpkgs, err := packages.Load(cfg, \"std\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tprog := ir.NewProgram(pkgs[0].Fset, ir.GlobalDebug)\n\t\tseen := map[*packages.Package]struct{}{}\n\t\tvar create func(pkg *packages.Package)\n\t\tcreate = func(pkg *packages.Package) {\n\t\t\tif _, ok := seen[pkg]; ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tseen[pkg] = struct{}{}\n\t\t\tprog.CreatePackage(pkg.Types, pkg.Syntax, pkg.TypesInfo, true)\n\t\t\tfor _, imp := range pkg.Imports {\n\t\t\t\tcreate(imp)\n\t\t\t}\n\t\t}\n\t\tfor _, pkg := range pkgs {\n\t\t\tcreate(pkg)\n\t\t}\n\t\tprog.Build()\n\t}\n}\n"
  },
  {
    "path": "go/ir/blockopt.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// Simple block optimizations to simplify the control flow graph.\n\n// TODO(adonovan): opt: instead of creating several \"unreachable\" blocks\n// per function in the Builder, reuse a single one (e.g. at Blocks[1])\n// to reduce garbage.\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\n// If true, perform sanity checking and show progress at each\n// successive iteration of optimizeBlocks.  Very verbose.\nconst debugBlockOpt = false\n\n// markReachable sets Index=-1 for all blocks reachable from b.\nfunc markReachable(b *BasicBlock) {\n\tb.gaps = -1\n\tfor _, succ := range b.Succs {\n\t\tif succ.gaps == 0 {\n\t\t\tmarkReachable(succ)\n\t\t}\n\t}\n}\n\n// deleteUnreachableBlocks marks all reachable blocks of f and\n// eliminates (nils) all others, including possibly cyclic subgraphs.\nfunc deleteUnreachableBlocks(f *Function) {\n\tconst white, black = 0, -1\n\t// We borrow b.gaps temporarily as the mark bit.\n\tfor _, b := range f.Blocks {\n\t\tb.gaps = white\n\t}\n\tmarkReachable(f.Blocks[0])\n\t// In SSI form, we need the exit to be reachable for correct\n\t// post-dominance information. In original form, however, we\n\t// cannot unconditionally mark it reachable because we won't\n\t// be adding fake edges, and this breaks the calculation of\n\t// dominance information.\n\tmarkReachable(f.Exit)\n\tfor i, b := range f.Blocks {\n\t\tif b.gaps == white {\n\t\t\tfor _, c := range b.Succs {\n\t\t\t\tif c.gaps == black {\n\t\t\t\t\tc.removePred(b) // delete white->black edge\n\t\t\t\t}\n\t\t\t}\n\t\t\tif debugBlockOpt {\n\t\t\t\tfmt.Fprintln(os.Stderr, \"unreachable\", b)\n\t\t\t}\n\t\t\tf.Blocks[i] = nil // delete b\n\t\t}\n\t}\n\tf.removeNilBlocks()\n}\n\n// jumpThreading attempts to apply simple jump-threading to block b,\n// in which a->b->c become a->c if b is just a Jump.\n// The result is true if the optimization was applied.\nfunc jumpThreading(f *Function, b *BasicBlock) bool {\n\tif b.Index == 0 {\n\t\treturn false // don't apply to entry block\n\t}\n\tif b.Instrs == nil {\n\t\treturn false\n\t}\n\tfor _, pred := range b.Preds {\n\t\tswitch pred.Control().(type) {\n\t\tcase *ConstantSwitch:\n\t\t\t// don't optimize away the head blocks of switch statements\n\t\t\treturn false\n\t\t}\n\t}\n\tif _, ok := b.Instrs[0].(*Jump); !ok {\n\t\treturn false // not just a jump\n\t}\n\tc := b.Succs[0]\n\tif c == b {\n\t\treturn false // don't apply to degenerate jump-to-self.\n\t}\n\tif c.hasPhi() {\n\t\treturn false // not sound without more effort\n\t}\n\tfor j, a := range b.Preds {\n\t\ta.replaceSucc(b, c)\n\n\t\t// If a now has two edges to c, replace its degenerate If by Jump.\n\t\tif len(a.Succs) == 2 && a.Succs[0] == c && a.Succs[1] == c {\n\t\t\tjump := new(Jump)\n\t\t\tjump.setBlock(a)\n\t\t\ta.Instrs[len(a.Instrs)-1] = jump\n\t\t\ta.Succs = a.Succs[:1]\n\t\t\tc.removePred(b)\n\t\t} else {\n\t\t\tif j == 0 {\n\t\t\t\tc.replacePred(b, a)\n\t\t\t} else {\n\t\t\t\tc.Preds = append(c.Preds, a)\n\t\t\t}\n\t\t}\n\n\t\tif debugBlockOpt {\n\t\t\tfmt.Fprintln(os.Stderr, \"jumpThreading\", a, b, c)\n\t\t}\n\t}\n\tf.Blocks[b.Index] = nil // delete b\n\treturn true\n}\n\n// fuseBlocks attempts to apply the block fusion optimization to block\n// a, in which a->b becomes ab if len(a.Succs)==len(b.Preds)==1.\n// The result is true if the optimization was applied.\nfunc fuseBlocks(f *Function, a *BasicBlock) bool {\n\tif len(a.Succs) != 1 {\n\t\treturn false\n\t}\n\tif a.Succs[0] == f.Exit {\n\t\treturn false\n\t}\n\tb := a.Succs[0]\n\tif len(b.Preds) != 1 {\n\t\treturn false\n\t}\n\tif _, ok := a.Instrs[len(a.Instrs)-1].(*Panic); ok {\n\t\t// panics aren't simple jumps, they have side effects.\n\t\treturn false\n\t}\n\n\t// Degenerate &&/|| ops may result in a straight-line CFG\n\t// containing φ-nodes. (Ideally we'd replace such them with\n\t// their sole operand but that requires Referrers, built later.)\n\tif b.hasPhi() {\n\t\treturn false // not sound without further effort\n\t}\n\n\t// Eliminate jump at end of A, then copy all of B across.\n\ta.Instrs = append(a.Instrs[:len(a.Instrs)-1], b.Instrs...)\n\tfor _, instr := range b.Instrs {\n\t\tinstr.setBlock(a)\n\t}\n\n\t// A inherits B's successors\n\ta.Succs = append(a.succs2[:0], b.Succs...)\n\n\t// Fix up Preds links of all successors of B.\n\tfor _, c := range b.Succs {\n\t\tc.replacePred(b, a)\n\t}\n\n\tif debugBlockOpt {\n\t\tfmt.Fprintln(os.Stderr, \"fuseBlocks\", a, b)\n\t}\n\n\tf.Blocks[b.Index] = nil // delete b\n\treturn true\n}\n\n// optimizeBlocks() performs some simple block optimizations on a\n// completed function: dead block elimination, block fusion, jump\n// threading.\nfunc optimizeBlocks(f *Function) {\n\tif debugBlockOpt {\n\t\tf.WriteTo(os.Stderr)\n\t\tmustSanityCheck(f, nil)\n\t}\n\n\tdeleteUnreachableBlocks(f)\n\n\t// Loop until no further progress.\n\tchanged := true\n\tfor changed {\n\t\tchanged = false\n\n\t\tif debugBlockOpt {\n\t\t\tf.WriteTo(os.Stderr)\n\t\t\tmustSanityCheck(f, nil)\n\t\t}\n\n\t\tfor _, b := range f.Blocks {\n\t\t\t// f.Blocks will temporarily contain nils to indicate\n\t\t\t// deleted blocks; we remove them at the end.\n\t\t\tif b == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Fuse blocks.  b->c becomes bc.\n\t\t\tif fuseBlocks(f, b) {\n\t\t\t\tchanged = true\n\t\t\t}\n\n\t\t\t// a->b->c becomes a->c if b contains only a Jump.\n\t\t\tif jumpThreading(f, b) {\n\t\t\t\tchanged = true\n\t\t\t\tcontinue // (b was disconnected)\n\t\t\t}\n\t\t}\n\t}\n\tf.removeNilBlocks()\n}\n"
  },
  {
    "path": "go/ir/builder.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file implements the BUILD phase of IR construction.\n//\n// IR construction has two phases, CREATE and BUILD.  In the CREATE phase\n// (create.go), all packages are constructed and type-checked and\n// definitions of all package members are created, method-sets are\n// computed, and wrapper methods are synthesized.\n// ir.Packages are created in arbitrary order.\n//\n// In the BUILD phase (builder.go), the builder traverses the AST of\n// each Go source function and generates IR instructions for the\n// function body.  Initializer expressions for package-level variables\n// are emitted to the package's init() function in the order specified\n// by go/types.Info.InitOrder, then code for each function in the\n// package is generated in lexical order.\n//\n// The builder's and Program's indices (maps) are populated and\n// mutated during the CREATE phase, but during the BUILD phase they\n// remain constant.  The sole exception is Prog.methodSets and its\n// related maps, which are protected by a dedicated mutex.\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"go/version\"\n\t\"os\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/exp/typeparams\"\n)\n\nvar (\n\tvarOk    = newVar(\"ok\", tBool)\n\tvarIndex = newVar(\"index\", tInt)\n\n\t// Type constants.\n\ttBool       = types.Typ[types.Bool]\n\ttInt        = types.Typ[types.Int]\n\ttInvalid    = types.Typ[types.Invalid]\n\ttString     = types.Typ[types.String]\n\ttUntypedNil = types.Typ[types.UntypedNil]\n\ttEface      = types.NewInterfaceType(nil, nil).Complete()\n\ttDeferStack = types.NewPointer(typeutil.NewDeferStack())\n\n\tvDeferStack = &Builtin{\n\t\tname: \"ssa:deferstack\",\n\t\tsig:  types.NewSignatureType(nil, nil, nil, nil, types.NewTuple(anonVar(tDeferStack)), false),\n\t}\n)\n\n// range-over-func jump is READY\nfunc jReady() *Const {\n\tc := intConst(0, nil)\n\tc.comment = \"rangefunc.exit.ready\"\n\treturn c\n}\n\n// range-over-func jump is BUSY\nfunc jBusy() *Const {\n\tc := intConst(-1, nil)\n\tc.comment = \"rangefunc.exit.busy\"\n\treturn c\n}\n\n// range-over-func jump is DONE\nfunc jDone() *Const {\n\tc := intConst(-2, nil)\n\tc.comment = \"rangefunc.exit.done\"\n\treturn c\n}\n\n// builder holds state associated with the package currently being built.\n// Its methods contain all the logic for AST-to-IR conversion.\ntype builder struct {\n\tprintFunc string\n\n\tblocksets [5]BlockSet\n}\n\n// cond emits to fn code to evaluate boolean condition e and jump\n// to t or f depending on its value, performing various simplifications.\n//\n// Postcondition: fn.currentBlock is nil.\nfunc (b *builder) cond(fn *Function, e ast.Expr, t, f *BasicBlock) *If {\n\tswitch e := e.(type) {\n\tcase *ast.ParenExpr:\n\t\treturn b.cond(fn, e.X, t, f)\n\n\tcase *ast.BinaryExpr:\n\t\tswitch e.Op {\n\t\tcase token.LAND:\n\t\t\tltrue := fn.newBasicBlock(\"cond.true\")\n\t\t\tb.cond(fn, e.X, ltrue, f)\n\t\t\tfn.currentBlock = ltrue\n\t\t\treturn b.cond(fn, e.Y, t, f)\n\n\t\tcase token.LOR:\n\t\t\tlfalse := fn.newBasicBlock(\"cond.false\")\n\t\t\tb.cond(fn, e.X, t, lfalse)\n\t\t\tfn.currentBlock = lfalse\n\t\t\treturn b.cond(fn, e.Y, t, f)\n\t\t}\n\n\tcase *ast.UnaryExpr:\n\t\tif e.Op == token.NOT {\n\t\t\treturn b.cond(fn, e.X, f, t)\n\t\t}\n\t}\n\n\t// A traditional compiler would simplify \"if false\" (etc) here\n\t// but we do not, for better fidelity to the source code.\n\t//\n\t// The value of a constant condition may be platform-specific,\n\t// and may cause blocks that are reachable in some configuration\n\t// to be hidden from subsequent analyses such as bug-finding tools.\n\treturn emitIf(fn, b.expr(fn, e), t, f, e)\n}\n\n// logicalBinop emits code to fn to evaluate e, a &&- or\n// ||-expression whose reified boolean value is wanted.\n// The value is returned.\nfunc (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value {\n\trhs := fn.newBasicBlock(\"binop.rhs\")\n\tdone := fn.newBasicBlock(\"binop.done\")\n\n\t// T(e) = T(e.X) = T(e.Y) after untyped constants have been\n\t// eliminated.\n\t// TODO(adonovan): not true; MyBool==MyBool yields UntypedBool.\n\tt := fn.Pkg.typeOf(e)\n\n\tvar short Value // value of the short-circuit path\n\tswitch e.Op {\n\tcase token.LAND:\n\t\tb.cond(fn, e.X, rhs, done)\n\t\tshort = emitConst(fn, NewConst(constant.MakeBool(false), t, e))\n\n\tcase token.LOR:\n\t\tb.cond(fn, e.X, done, rhs)\n\t\tshort = emitConst(fn, NewConst(constant.MakeBool(true), t, e))\n\t}\n\n\t// Is rhs unreachable?\n\tif rhs.Preds == nil {\n\t\t// Simplify false&&y to false, true||y to true.\n\t\tfn.currentBlock = done\n\t\treturn short\n\t}\n\n\t// Is done unreachable?\n\tif done.Preds == nil {\n\t\t// Simplify true&&y (or false||y) to y.\n\t\tfn.currentBlock = rhs\n\t\treturn b.expr(fn, e.Y)\n\t}\n\n\t// All edges from e.X to done carry the short-circuit value.\n\tvar edges []Value\n\tfor range done.Preds {\n\t\tedges = append(edges, short)\n\t}\n\n\t// The edge from e.Y to done carries the value of e.Y.\n\tfn.currentBlock = rhs\n\tedges = append(edges, b.expr(fn, e.Y))\n\temitJump(fn, done, e)\n\tfn.currentBlock = done\n\n\tphi := &Phi{Edges: edges}\n\tphi.typ = t\n\tphi.comment = e.Op.String()\n\treturn done.emit(phi, e)\n}\n\n// exprN lowers a multi-result expression e to IR form, emitting code\n// to fn and returning a single Value whose type is a *types.Tuple.\n// The caller must access the components via Extract.\n//\n// Multi-result expressions include CallExprs in a multi-value\n// assignment or return statement, and \"value,ok\" uses of\n// TypeAssertExpr, IndexExpr (when X is a map), and Recv.\nfunc (b *builder) exprN(fn *Function, e ast.Expr) Value {\n\ttyp := fn.Pkg.typeOf(e).(*types.Tuple)\n\tswitch e := e.(type) {\n\tcase *ast.ParenExpr:\n\t\treturn b.exprN(fn, e.X)\n\n\tcase *ast.CallExpr:\n\t\t// Currently, no built-in function nor type conversion\n\t\t// has multiple results, so we can avoid some of the\n\t\t// cases for single-valued CallExpr.\n\t\tvar c Call\n\t\tb.setCall(fn, e, &c.Call)\n\t\tc.typ = typ\n\t\treturn emitCall(fn, &c, e)\n\n\tcase *ast.IndexExpr:\n\t\tmapt := typeutil.CoreType(fn.Pkg.typeOf(e.X)).Underlying().(*types.Map)\n\t\tlookup := &MapLookup{\n\t\t\tX:       b.expr(fn, e.X),\n\t\t\tIndex:   emitConv(fn, b.expr(fn, e.Index), mapt.Key(), e),\n\t\t\tCommaOk: true,\n\t\t}\n\t\tlookup.setType(typ)\n\t\treturn fn.emit(lookup, e)\n\n\tcase *ast.TypeAssertExpr:\n\t\treturn emitTypeTest(fn, b.expr(fn, e.X), typ.At(0).Type(), e)\n\n\tcase *ast.UnaryExpr: // must be receive <-\n\t\treturn emitRecv(fn, b.expr(fn, e.X), true, typ, e)\n\t}\n\tpanic(fmt.Sprintf(\"exprN(%T) in %s\", e, fn))\n}\n\n// builtin emits to fn IR instructions to implement a call to the\n// built-in function obj with the specified arguments\n// and return type.  It returns the value defined by the result.\n//\n// The result is nil if no special handling was required; in this case\n// the caller should treat this like an ordinary library function\n// call.\nfunc (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ types.Type, source ast.Node) Value {\n\tswitch obj.Name() {\n\tcase \"make\":\n\t\tstyp := typ.Underlying()\n\t\tif _, ok := typ.Underlying().(*types.Interface); ok {\n\t\t\t// This must be a type parameter with a core type.\n\t\t\t// Set styp to the core type and generate instructions based on it.\n\t\t\tassert(typeparams.IsTypeParam(typ))\n\t\t\tstyp = typeutil.CoreType(typ)\n\t\t\tassert(styp != nil)\n\t\t}\n\t\tswitch styp.(type) {\n\t\tcase *types.Slice:\n\t\t\tn := b.expr(fn, args[1])\n\t\t\tm := n\n\t\t\tif len(args) == 3 {\n\t\t\t\tm = b.expr(fn, args[2])\n\t\t\t}\n\t\t\tif m, ok := m.(*Const); ok {\n\t\t\t\t// treat make([]T, n, m) as new([m]T)[:n]\n\t\t\t\tcap := m.Int64()\n\t\t\t\tat := types.NewArray(styp.Underlying().(*types.Slice).Elem(), cap)\n\t\t\t\tv := &Slice{\n\t\t\t\t\tX:    emitNew(fn, at, source, \"makeslice\"),\n\t\t\t\t\tHigh: n,\n\t\t\t\t}\n\t\t\t\tv.setType(typ)\n\t\t\t\treturn fn.emit(v, source)\n\t\t\t}\n\t\t\tv := &MakeSlice{\n\t\t\t\tLen: n,\n\t\t\t\tCap: m,\n\t\t\t}\n\t\t\tv.setType(typ)\n\t\t\treturn fn.emit(v, source)\n\n\t\tcase *types.Map:\n\t\t\tvar res Value\n\t\t\tif len(args) == 2 {\n\t\t\t\tres = b.expr(fn, args[1])\n\t\t\t}\n\t\t\tv := &MakeMap{Reserve: res}\n\t\t\tv.setType(typ)\n\t\t\treturn fn.emit(v, source)\n\n\t\tcase *types.Chan:\n\t\t\tvar sz Value = emitConst(fn, intConst(0, source))\n\t\t\tif len(args) == 2 {\n\t\t\t\tsz = b.expr(fn, args[1])\n\t\t\t}\n\t\t\tv := &MakeChan{Size: sz}\n\t\t\tv.setType(typ)\n\t\t\treturn fn.emit(v, source)\n\n\t\tdefault:\n\t\t\tlint.ExhaustiveTypeSwitch(typ.Underlying())\n\t\t}\n\n\tcase \"new\":\n\t\talloc := emitNew(fn, deref(typ), source, \"new\")\n\t\tif !fn.Pkg.info.Types[args[0]].IsType() {\n\t\t\tv := b.expr(fn, args[0])\n\t\t\temitStore(fn, alloc, v, source)\n\t\t}\n\t\treturn alloc\n\n\tcase \"len\", \"cap\":\n\t\t// Special case: len or cap of an array or *array is based on the type, not the value which may be nil. We must\n\t\t// still evaluate the value, though. (If it was side-effect free, the whole call would have been\n\t\t// constant-folded.)\n\t\t//\n\t\t// For example, for len(gen()), we need to evaluate gen() for its side-effects, but don't need the returned\n\t\t// value to determine the length of the array, which is constant.\n\t\t//\n\t\t// Technically this shouldn't apply to type parameters because their length/capacity is never constant. We still\n\t\t// choose to treat them as constant so that users of the IR get the practically constant length for free.\n\t\tt := typeutil.CoreType(deref(fn.Pkg.typeOf(args[0])))\n\t\tif at, ok := t.(*types.Array); ok {\n\t\t\tb.expr(fn, args[0]) // for effects only\n\t\t\treturn emitConst(fn, intConst(at.Len(), args[0]))\n\t\t}\n\t\t// Otherwise treat as normal.\n\n\tcase \"panic\":\n\t\tfn.emit(&Panic{\n\t\t\tX: emitConv(fn, b.expr(fn, args[0]), tEface, source),\n\t\t}, source)\n\t\taddEdge(fn.currentBlock, fn.Exit)\n\t\tfn.currentBlock = fn.newBasicBlock(\"unreachable\")\n\t\treturn emitConst(fn, NewConst(constant.MakeBool(true), tBool, nil)) // any non-nil Value will do\n\t}\n\treturn nil // treat all others as a regular function call\n}\n\n// addr lowers a single-result addressable expression e to IR form,\n// emitting code to fn and returning the location (an lvalue) defined\n// by the expression.\n//\n// If escaping is true, addr marks the base variable of the\n// addressable expression e as being a potentially escaping pointer\n// value.  For example, in this code:\n//\n//\ta := A{\n//\t  b: [1]B{B{c: 1}}\n//\t}\n//\treturn &a.b[0].c\n//\n// the application of & causes a.b[0].c to have its address taken,\n// which means that ultimately the local variable a must be\n// heap-allocated.  This is a simple but very conservative escape\n// analysis.\n//\n// Operations forming potentially escaping pointers include:\n// - &x, including when implicit in method call or composite literals.\n// - a[:] iff a is an array (not *array)\n// - references to variables in lexically enclosing functions.\nfunc (b *builder) addr(fn *Function, e ast.Expr, escaping bool) (RET lvalue) {\n\tswitch e := e.(type) {\n\tcase *ast.Ident:\n\t\tif isBlankIdent(e) {\n\t\t\treturn blank{}\n\t\t}\n\t\tobj := fn.Pkg.objectOf(e)\n\t\tv := fn.Prog.packageLevelValue(obj) // var (address)\n\t\tif v == nil {\n\t\t\tv = fn.lookup(obj.(*types.Var), escaping)\n\t\t}\n\t\treturn &address{addr: v, expr: e}\n\n\tcase *ast.CompositeLit:\n\t\tt := deref(fn.Pkg.typeOf(e))\n\t\tvar v *Alloc\n\t\tif escaping {\n\t\t\tv = emitNew(fn, t, e, \"complit\")\n\t\t} else {\n\t\t\tv = emitLocal(fn, t, e, \"complit\")\n\t\t}\n\t\tvar sb storebuf\n\t\tb.compLit(fn, v, e, true, &sb)\n\t\tsb.emit(fn)\n\t\treturn &address{addr: v, expr: e}\n\n\tcase *ast.ParenExpr:\n\t\treturn b.addr(fn, e.X, escaping)\n\n\tcase *ast.SelectorExpr:\n\t\tsel, ok := fn.Pkg.info.Selections[e]\n\t\tif !ok {\n\t\t\t// qualified identifier\n\t\t\treturn b.addr(fn, e.Sel, escaping)\n\t\t}\n\t\tif sel.Kind() != types.FieldVal {\n\t\t\tpanic(sel)\n\t\t}\n\t\twantAddr := true\n\t\tv := b.receiver(fn, e.X, wantAddr, escaping, sel, e)\n\t\tindex := sel.Index()[len(sel.Index())-1]\n\t\tvut := typeutil.CoreType(deref(v.Type())).Underlying().(*types.Struct)\n\t\tfld := vut.Field(index)\n\t\t// Due to the two phases of resolving AssignStmt, a panic from x.f = p()\n\t\t// when x is nil is required to come after the side-effects of\n\t\t// evaluating x and p().\n\t\temit := func(fn *Function) Value {\n\t\t\treturn emitFieldSelection(fn, v, index, true, e.Sel)\n\t\t}\n\t\treturn &lazyAddress{addr: emit, t: fld.Type(), expr: e.Sel}\n\n\tcase *ast.IndexExpr:\n\t\tvar x Value\n\t\tvar et types.Type\n\t\txt := fn.Pkg.typeOf(e.X)\n\n\t\t// Indexing doesn't need a core type, it only requires all types to be similar enough. For example, []int64 |\n\t\t// [5]int64 can be indexed. The element types do have to match though.\n\n\t\tterms, err := typeparams.NormalTerms(xt)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Sprintf(\"unexpected error: %s\", err))\n\t\t}\n\t\tisArrayLike := func() (types.Type, bool) {\n\t\t\tfor _, term := range terms {\n\t\t\t\tarr, ok := term.Type().Underlying().(*types.Array)\n\t\t\t\tif ok {\n\t\t\t\t\treturn arr.Elem(), true\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil, false\n\t\t}\n\n\t\tisSliceLike := func() (types.Type, bool) {\n\t\t\tfor _, term := range terms {\n\t\t\t\tswitch t := term.Type().Underlying().(type) {\n\t\t\t\tcase *types.Slice:\n\t\t\t\t\treturn t.Elem(), true\n\t\t\t\tcase *types.Pointer:\n\t\t\t\t\treturn t.Elem().Underlying().(*types.Array).Elem(), true\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil, false\n\t\t}\n\n\t\tif elem, ok := isArrayLike(); ok {\n\t\t\t// array\n\t\t\tx = b.addr(fn, e.X, escaping).address(fn)\n\t\t\tet = types.NewPointer(elem)\n\t\t} else if elem, ok := isSliceLike(); ok {\n\t\t\t// slice or *array\n\t\t\tx = b.expr(fn, e.X)\n\t\t\tet = types.NewPointer(elem)\n\t\t} else if t, ok := typeutil.CoreType(xt).Underlying().(*types.Map); ok {\n\t\t\treturn &element{\n\t\t\t\tm: b.expr(fn, e.X),\n\t\t\t\tk: emitConv(fn, b.expr(fn, e.Index), t.Key(), e.Index),\n\t\t\t\tt: t.Elem(),\n\t\t\t}\n\t\t} else {\n\t\t\tpanic(\"unexpected container type in IndexExpr: \" + t.String())\n\t\t}\n\n\t\t// Due to the two phases of resolving AssignStmt, a panic from x[i] = p()\n\t\t// when x is nil or i is out-of-bounds is required to come after the\n\t\t// side-effects of evaluating x, i and p().\n\t\tindex := b.expr(fn, e.Index)\n\t\temit := func(fn *Function) Value {\n\t\t\tv := &IndexAddr{\n\t\t\t\tX:     x,\n\t\t\t\tIndex: index,\n\t\t\t}\n\t\t\tv.setType(et)\n\t\t\treturn fn.emit(v, e)\n\t\t}\n\t\treturn &lazyAddress{addr: emit, t: deref(et), expr: e}\n\n\tcase *ast.StarExpr:\n\t\treturn &address{addr: b.expr(fn, e.X), expr: e}\n\t}\n\n\tpanic(fmt.Sprintf(\"unexpected address expression: %T\", e))\n}\n\ntype store struct {\n\tlhs    lvalue\n\trhs    Value\n\tsource ast.Node\n\n\t// if debugRef is set no other fields will be set\n\tdebugRef *DebugRef\n}\n\ntype storebuf struct{ stores []store }\n\nfunc (sb *storebuf) store(lhs lvalue, rhs Value, source ast.Node) {\n\tsb.stores = append(sb.stores, store{lhs, rhs, source, nil})\n}\n\nfunc (sb *storebuf) storeDebugRef(ref *DebugRef) {\n\tsb.stores = append(sb.stores, store{debugRef: ref})\n}\n\nfunc (sb *storebuf) emit(fn *Function) {\n\tfor _, s := range sb.stores {\n\t\tif s.debugRef == nil {\n\t\t\ts.lhs.store(fn, s.rhs, s.source)\n\t\t} else {\n\t\t\tfn.emit(s.debugRef, nil)\n\t\t}\n\t}\n}\n\n// assign emits to fn code to initialize the lvalue loc with the value\n// of expression e.  If isZero is true, assign assumes that loc holds\n// the zero value for its type.\n//\n// This is equivalent to loc.store(fn, b.expr(fn, e)), but may generate\n// better code in some cases, e.g., for composite literals in an\n// addressable location.\n//\n// If sb is not nil, assign generates code to evaluate expression e, but\n// not to update loc.  Instead, the necessary stores are appended to the\n// storebuf sb so that they can be executed later.  This allows correct\n// in-place update of existing variables when the RHS is a composite\n// literal that may reference parts of the LHS.\nfunc (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *storebuf, source ast.Node) {\n\t// Can we initialize it in place?\n\tif e, ok := unparen(e).(*ast.CompositeLit); ok {\n\t\t// A CompositeLit never evaluates to a pointer,\n\t\t// so if the type of the location is a pointer,\n\t\t// an &-operation is implied.\n\t\tif _, ok := loc.(blank); !ok { // avoid calling blank.typ()\n\t\t\tif isPointer(loc.typ()) {\n\t\t\t\t// Example input that hits this code:\n\t\t\t\t//\n\t\t\t\t// \ttype S1 struct{ X int }\n\t\t\t\t// \tx := []*S1{\n\t\t\t\t// \t\t{1}, // <-- & is implied\n\t\t\t\t// \t}\n\t\t\t\t// \t_ = x\n\t\t\t\tptr := b.addr(fn, e, true).address(fn)\n\t\t\t\t// copy address\n\t\t\t\tif sb != nil {\n\t\t\t\t\tsb.store(loc, ptr, source)\n\t\t\t\t} else {\n\t\t\t\t\tloc.store(fn, ptr, source)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif _, ok := loc.(*address); ok {\n\t\t\tif types.IsInterface(loc.typ()) && !typeparams.IsTypeParam(loc.typ()) {\n\t\t\t\t// e.g. var x interface{} = T{...}\n\t\t\t\t// Can't in-place initialize an interface value.\n\t\t\t\t// Fall back to copying.\n\t\t\t} else {\n\t\t\t\t// x = T{...} or x := T{...}\n\t\t\t\taddr := loc.address(fn)\n\t\t\t\tif sb != nil {\n\t\t\t\t\tb.compLit(fn, addr, e, isZero, sb)\n\t\t\t\t} else {\n\t\t\t\t\tvar sb storebuf\n\t\t\t\t\tb.compLit(fn, addr, e, isZero, &sb)\n\t\t\t\t\tsb.emit(fn)\n\t\t\t\t}\n\n\t\t\t\t// Subtle: emit debug ref for aggregate types only;\n\t\t\t\t// slice and map are handled by store ops in compLit.\n\t\t\t\tswitch typeutil.CoreType(loc.typ()).Underlying().(type) {\n\t\t\t\tcase *types.Struct, *types.Array:\n\t\t\t\t\tif sb != nil {\n\t\t\t\t\t\t// Make sure we don't emit DebugRefs before the store has actually occurred\n\t\t\t\t\t\tif ref := makeDebugRef(fn, e, addr, true); ref != nil {\n\t\t\t\t\t\t\tsb.storeDebugRef(ref)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\temitDebugRef(fn, e, addr, true)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\t// simple case: just copy\n\trhs := b.expr(fn, e)\n\tif sb != nil {\n\t\tsb.store(loc, rhs, source)\n\t} else {\n\t\tloc.store(fn, rhs, source)\n\t}\n}\n\n// expr lowers a single-result expression e to IR form, emitting code\n// to fn and returning the Value defined by the expression.\nfunc (b *builder) expr(fn *Function, e ast.Expr) Value {\n\te = unparen(e)\n\n\ttv := fn.Pkg.info.Types[e]\n\n\t// Is expression a constant?\n\tif tv.Value != nil {\n\t\treturn emitConst(fn, NewConst(tv.Value, tv.Type, e))\n\t}\n\n\tvar v Value\n\tif tv.Addressable() {\n\t\t// Prefer pointer arithmetic ({Index,Field}Addr) followed\n\t\t// by Load over subelement extraction (e.g. Index, Field),\n\t\t// to avoid large copies.\n\t\tv = b.addr(fn, e, false).load(fn, e)\n\t} else {\n\t\tv = b.expr0(fn, e, tv)\n\t}\n\tif fn.debugInfo() {\n\t\temitDebugRef(fn, e, v, false)\n\t}\n\treturn v\n}\n\nfunc (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {\n\tswitch e := e.(type) {\n\tcase *ast.BasicLit:\n\t\tpanic(\"non-constant BasicLit\") // unreachable\n\n\tcase *ast.FuncLit:\n\t\tfn2 := &Function{\n\t\t\tname:         fmt.Sprintf(\"%s$%d\", fn.Name(), 1+len(fn.AnonFuncs)),\n\t\t\tSignature:    fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature),\n\t\t\tparent:       fn,\n\t\t\tPkg:          fn.Pkg,\n\t\t\tProg:         fn.Prog,\n\t\t\tfunctionBody: new(functionBody),\n\t\t\tgoversion:    fn.goversion, // share the parent's goversion\n\t\t}\n\t\tfn2.uniq = fn.uniq // start from parent's unique values\n\t\tfn2.source = e\n\t\tfn.AnonFuncs = append(fn.AnonFuncs, fn2)\n\t\tfn2.initHTML(b.printFunc)\n\t\tb.buildFunction(fn2)\n\t\tfn.uniq = fn2.uniq // resume after anon's unique values\n\t\tif fn2.FreeVars == nil {\n\t\t\treturn fn2\n\t\t}\n\t\tv := &MakeClosure{Fn: fn2}\n\t\tv.setType(tv.Type)\n\t\tfor _, fv := range fn2.FreeVars {\n\t\t\tv.Bindings = append(v.Bindings, fv.outer)\n\t\t\tfv.outer = nil\n\t\t}\n\t\treturn fn.emit(v, e)\n\n\tcase *ast.TypeAssertExpr: // single-result form only\n\t\treturn emitTypeAssert(fn, b.expr(fn, e.X), tv.Type, e)\n\n\tcase *ast.CallExpr:\n\t\tif fn.Pkg.info.Types[e.Fun].IsType() {\n\t\t\t// Explicit type conversion, e.g. string(x) or big.Int(x)\n\t\t\tx := b.expr(fn, e.Args[0])\n\t\t\ty := emitConv(fn, x, tv.Type, e)\n\t\t\treturn y\n\t\t}\n\t\t// Call to \"intrinsic\" built-ins, e.g. new, make, panic.\n\t\tif id, ok := unparen(e.Fun).(*ast.Ident); ok {\n\t\t\tif obj, ok := fn.Pkg.info.Uses[id].(*types.Builtin); ok {\n\t\t\t\tif v := b.builtin(fn, obj, e.Args, tv.Type, e); v != nil {\n\t\t\t\t\treturn v\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Regular function call.\n\t\tvar v Call\n\t\tb.setCall(fn, e, &v.Call)\n\t\tv.setType(tv.Type)\n\t\treturn emitCall(fn, &v, e)\n\n\tcase *ast.UnaryExpr:\n\t\tswitch e.Op {\n\t\tcase token.AND: // &X --- potentially escaping.\n\t\t\taddr := b.addr(fn, e.X, true)\n\t\t\tif _, ok := unparen(e.X).(*ast.StarExpr); ok {\n\t\t\t\t// &*p must panic if p is nil (https://golang.org/s/go12nil).\n\t\t\t\t// For simplicity, we'll just (suboptimally) rely\n\t\t\t\t// on the side effects of a load.\n\t\t\t\t// TODO(adonovan): emit dedicated nilcheck.\n\t\t\t\taddr.load(fn, e)\n\t\t\t}\n\t\t\treturn addr.address(fn)\n\t\tcase token.ADD:\n\t\t\treturn b.expr(fn, e.X)\n\t\tcase token.NOT, token.SUB, token.XOR: // ! <- - ^\n\t\t\tv := &UnOp{\n\t\t\t\tOp: e.Op,\n\t\t\t\tX:  b.expr(fn, e.X),\n\t\t\t}\n\t\t\tv.setType(tv.Type)\n\t\t\treturn fn.emit(v, e)\n\t\tcase token.ARROW:\n\t\t\treturn emitRecv(fn, b.expr(fn, e.X), false, tv.Type, e)\n\t\tdefault:\n\t\t\tpanic(e.Op)\n\t\t}\n\n\tcase *ast.BinaryExpr:\n\t\tswitch e.Op {\n\t\tcase token.LAND, token.LOR:\n\t\t\treturn b.logicalBinop(fn, e)\n\t\tcase token.SHL, token.SHR:\n\t\t\tfallthrough\n\t\tcase token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:\n\t\t\treturn emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), tv.Type, e)\n\n\t\tcase token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ:\n\t\t\tcmp := emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e)\n\t\t\t// The type of x==y may be UntypedBool.\n\t\t\treturn emitConv(fn, cmp, types.Default(tv.Type), e)\n\t\tdefault:\n\t\t\tpanic(\"illegal op in BinaryExpr: \" + e.Op.String())\n\t\t}\n\n\tcase *ast.SliceExpr:\n\t\tvar x Value\n\t\tif core := typeutil.CoreType(fn.Pkg.typeOf(e.X)); core != nil {\n\t\t\tswitch core.Underlying().(type) {\n\t\t\tcase *types.Array:\n\t\t\t\t// Potentially escaping.\n\t\t\t\tx = b.addr(fn, e.X, true).address(fn)\n\t\t\tcase *types.Basic, *types.Slice, *types.Pointer: // *array\n\t\t\t\tx = b.expr(fn, e.X)\n\t\t\tdefault:\n\t\t\t\tpanic(\"unreachable\")\n\t\t\t}\n\t\t} else {\n\t\t\t// We're indexing a string | []byte. Note that other combinations such as []byte | [4]byte are currently not\n\t\t\t// allowed by the language.\n\t\t\tx = b.expr(fn, e.X)\n\t\t}\n\n\t\tvar low, high, max Value\n\t\tif e.Low != nil {\n\t\t\tlow = b.expr(fn, e.Low)\n\t\t}\n\t\tif e.High != nil {\n\t\t\thigh = b.expr(fn, e.High)\n\t\t}\n\t\tif e.Slice3 {\n\t\t\tmax = b.expr(fn, e.Max)\n\t\t}\n\t\tv := &Slice{\n\t\t\tX:    x,\n\t\t\tLow:  low,\n\t\t\tHigh: high,\n\t\t\tMax:  max,\n\t\t}\n\t\tv.setType(tv.Type)\n\t\treturn fn.emit(v, e)\n\n\tcase *ast.Ident:\n\t\tobj := fn.Pkg.info.Uses[e]\n\t\t// Universal built-in or nil?\n\t\tswitch obj := obj.(type) {\n\t\tcase *types.Builtin:\n\t\t\treturn &Builtin{name: obj.Name(), sig: tv.Type.(*types.Signature)}\n\t\tcase *types.Nil:\n\t\t\treturn emitConst(fn, nilConst(tv.Type, e))\n\t\t}\n\t\t// Package-level func or var?\n\t\tif v := fn.Prog.packageLevelValue(obj); v != nil {\n\t\t\tif _, ok := obj.(*types.Var); ok {\n\t\t\t\treturn emitLoad(fn, v, e) // var (address)\n\t\t\t}\n\t\t\tif instance, ok := fn.Pkg.info.Instances[e]; ok {\n\t\t\t\t// Instantiated generic function\n\t\t\t\treturn makeInstance(fn.Prog, v.(*Function), instance.Type.(*types.Signature), instance.TypeArgs)\n\t\t\t}\n\t\t\treturn v // (func)\n\t\t}\n\t\t// Local var.\n\t\treturn emitLoad(fn, fn.lookup(obj.(*types.Var), false), e) // var (address)\n\n\tcase *ast.SelectorExpr:\n\t\tsel, ok := fn.Pkg.info.Selections[e]\n\t\tif !ok {\n\t\t\t// builtin unsafe.{Add,Slice}\n\t\t\tif obj, ok := fn.Pkg.info.Uses[e.Sel].(*types.Builtin); ok {\n\t\t\t\treturn &Builtin{name: \"Unsafe\" + obj.Name(), sig: tv.Type.(*types.Signature)}\n\t\t\t}\n\t\t\t// qualified identifier\n\t\t\treturn b.expr(fn, e.Sel)\n\t\t}\n\t\tswitch sel.Kind() {\n\t\tcase types.MethodExpr:\n\t\t\t// (*T).f or T.f, the method f from the method-set of type T.\n\t\t\t// The result is a \"thunk\".\n\t\t\treturn emitConv(fn, makeThunk(fn.Prog, sel), tv.Type, e)\n\n\t\tcase types.MethodVal:\n\t\t\t// e.f where e is an expression and f is a method.\n\t\t\t// The result is a \"bound\".\n\t\t\tobj := sel.Obj().(*types.Func)\n\t\t\trt := recvType(obj)\n\t\t\twantAddr := isPointer(rt)\n\t\t\tescaping := true\n\t\t\tv := b.receiver(fn, e.X, wantAddr, escaping, sel, e)\n\t\t\tif types.IsInterface(rt) {\n\t\t\t\t// If v has interface type I,\n\t\t\t\t// we must emit a check that v is non-nil.\n\t\t\t\t// We use: typeassert v.(I).\n\t\t\t\temitTypeAssert(fn, v, rt, e)\n\t\t\t}\n\t\t\tc := &MakeClosure{\n\t\t\t\tFn:       makeBound(fn.Prog, obj),\n\t\t\t\tBindings: []Value{v},\n\t\t\t}\n\t\t\tc.source = e.Sel\n\t\t\tc.setType(tv.Type)\n\t\t\treturn fn.emit(c, e)\n\n\t\tcase types.FieldVal:\n\t\t\tindices := sel.Index()\n\t\t\tlast := len(indices) - 1\n\t\t\tv := b.expr(fn, e.X)\n\t\t\tv = emitImplicitSelections(fn, v, indices[:last], e)\n\t\t\tv = emitFieldSelection(fn, v, indices[last], false, e.Sel)\n\t\t\treturn v\n\t\t}\n\n\t\tpanic(\"unexpected expression-relative selector\")\n\n\tcase *ast.IndexExpr:\n\t\t// IndexExpr might either be an actual indexing operation, or an instantiation\n\t\txt := fn.Pkg.typeOf(e.X)\n\n\t\tterms, err := typeparams.NormalTerms(xt)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Sprintf(\"unexpected error: %s\", err))\n\t\t}\n\t\tisNonAddressableIndexable := func() (types.Type, bool) {\n\t\t\tfor _, term := range terms {\n\t\t\t\tswitch t := term.Type().Underlying().(type) {\n\t\t\t\tcase *types.Array:\n\t\t\t\t\treturn t.Elem(), true\n\t\t\t\tcase *types.Basic:\n\t\t\t\t\t// a string\n\t\t\t\t\treturn types.Universe.Lookup(\"byte\").Type(), true\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil, false\n\t\t}\n\n\t\tisAddressableIndexable := func() (types.Type, bool) {\n\t\t\tfor _, term := range terms {\n\t\t\t\tswitch t := term.Type().Underlying().(type) {\n\t\t\t\tcase *types.Slice:\n\t\t\t\t\treturn t.Elem(), true\n\t\t\t\tcase *types.Pointer:\n\t\t\t\t\treturn t.Elem().Underlying().(*types.Array).Elem(), true\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil, false\n\t\t}\n\n\t\tif elem, ok := isNonAddressableIndexable(); ok {\n\t\t\t// At least one of the types is non-addressable\n\t\t\tv := &Index{\n\t\t\t\tX:     b.expr(fn, e.X),\n\t\t\t\tIndex: b.expr(fn, e.Index),\n\t\t\t}\n\t\t\tv.setType(elem)\n\t\t\treturn fn.emit(v, e)\n\t\t} else if _, ok := isAddressableIndexable(); ok {\n\t\t\t// All types are addressable (otherwise the previous branch would've fired)\n\t\t\treturn b.addr(fn, e, false).load(fn, e)\n\t\t} else if t, ok := typeutil.CoreType(xt).Underlying().(*types.Map); ok {\n\t\t\t// Maps are not addressable.\n\t\t\tv := &MapLookup{\n\t\t\t\tX:     b.expr(fn, e.X),\n\t\t\t\tIndex: emitConv(fn, b.expr(fn, e.Index), t.Key(), e.Index),\n\t\t\t}\n\t\t\tv.setType(t.Elem())\n\t\t\treturn fn.emit(v, e)\n\t\t} else if _, ok := xt.Underlying().(*types.Signature); ok {\n\t\t\t// Instantiating a generic function\n\t\t\treturn b.expr(fn, e.X)\n\t\t} else {\n\t\t\tpanic(\"unexpected container type in IndexExpr: \" + t.String())\n\t\t}\n\n\tcase *ast.IndexListExpr:\n\t\t// Instantiating a generic function\n\t\treturn b.expr(fn, e.X)\n\n\tcase *ast.CompositeLit, *ast.StarExpr:\n\t\t// Addressable types (lvalues)\n\t\treturn b.addr(fn, e, false).load(fn, e)\n\t}\n\n\tpanic(fmt.Sprintf(\"unexpected expr: %T\", e))\n}\n\n// stmtList emits to fn code for all statements in list.\nfunc (b *builder) stmtList(fn *Function, list []ast.Stmt) {\n\tfor _, s := range list {\n\t\tb.stmt(fn, s)\n\t}\n}\n\n// receiver emits to fn code for expression e in the \"receiver\"\n// position of selection e.f (where f may be a field or a method) and\n// returns the effective receiver after applying the implicit field\n// selections of sel.\n//\n// wantAddr requests that the result is an address.  If\n// !sel.Indirect(), this may require that e be built in addr() mode; it\n// must thus be addressable.\n//\n// escaping is defined as per builder.addr().\nfunc (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *types.Selection, source ast.Node) Value {\n\tvar v Value\n\tif wantAddr && !sel.Indirect() && !isPointer(fn.Pkg.typeOf(e)) {\n\t\tv = b.addr(fn, e, escaping).address(fn)\n\t} else {\n\t\tv = b.expr(fn, e)\n\t}\n\n\tlast := len(sel.Index()) - 1\n\tv = emitImplicitSelections(fn, v, sel.Index()[:last], source)\n\tif !wantAddr && isPointer(v.Type()) {\n\t\tv = emitLoad(fn, v, e)\n\t}\n\treturn v\n}\n\n// setCallFunc populates the function parts of a CallCommon structure\n// (Func, Method, Recv, Args[0]) based on the kind of invocation\n// occurring in e.\nfunc (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {\n\t// Is this a method call?\n\tif selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok {\n\t\tsel, ok := fn.Pkg.info.Selections[selector]\n\t\tif ok && sel.Kind() == types.MethodVal {\n\t\t\tobj := sel.Obj().(*types.Func)\n\t\t\trecv := recvType(obj)\n\t\t\twantAddr := isPointer(recv)\n\t\t\tescaping := true\n\t\t\tv := b.receiver(fn, selector.X, wantAddr, escaping, sel, selector)\n\t\t\tif types.IsInterface(recv) {\n\t\t\t\t// Invoke-mode call.\n\n\t\t\t\t// Methods in interfaces cannot have their own type parameters, so we needn't do anything for type\n\t\t\t\t// parameters.\n\t\t\t\tc.Value = v\n\t\t\t\tc.Method = obj\n\t\t\t} else {\n\t\t\t\t// \"Call\"-mode call.\n\n\t\t\t\t// declaredFunc takes care of creating wrappers for functions with type parameters.\n\t\t\t\tc.Value = fn.Prog.declaredFunc(obj)\n\t\t\t\tc.Args = append(c.Args, v)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// sel.Kind()==MethodExpr indicates T.f() or (*T).f():\n\t\t// a statically dispatched call to the method f in the\n\t\t// method-set of T or *T.  T may be an interface.\n\t\t//\n\t\t// e.Fun would evaluate to a concrete method, interface\n\t\t// wrapper function, or promotion wrapper.\n\t\t//\n\t\t// For now, we evaluate it in the usual way.\n\t\t//\n\t\t// TODO(adonovan): opt: inline expr() here, to make the\n\t\t// call static and to avoid generation of wrappers.\n\t\t// It's somewhat tricky as it may consume the first\n\t\t// actual parameter if the call is \"invoke\" mode.\n\t\t//\n\t\t// Examples:\n\t\t//  type T struct{}; func (T) f() {}   // \"call\" mode\n\t\t//  type T interface { f() }           // \"invoke\" mode\n\t\t//\n\t\t//  type S struct{ T }\n\t\t//\n\t\t//  var s S\n\t\t//  S.f(s)\n\t\t//  (*S).f(&s)\n\t\t//\n\t\t// Suggested approach:\n\t\t// - consume the first actual parameter expression\n\t\t//   and build it with b.expr().\n\t\t// - apply implicit field selections.\n\t\t// - use MethodVal logic to populate fields of c.\n\t}\n\t// Evaluate the function operand in the usual way.\n\t//\n\t// Code in expr takes care of creating wrappers for functions with type parameters.\n\tc.Value = b.expr(fn, e.Fun)\n}\n\n// emitCallArgs emits to f code for the actual parameters of call e to\n// a (possibly built-in) function of effective type sig.\n// The argument values are appended to args, which is then returned.\nfunc (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallExpr, args []Value) []Value {\n\t// f(x, y, z...): pass slice z straight through.\n\tif e.Ellipsis != 0 {\n\t\tfor i, arg := range e.Args {\n\t\t\tv := emitConv(fn, b.expr(fn, arg), sig.Params().At(i).Type(), arg)\n\t\t\targs = append(args, v)\n\t\t}\n\t\treturn args\n\t}\n\n\toffset := len(args) // 1 if call has receiver, 0 otherwise\n\n\t// Evaluate actual parameter expressions.\n\t//\n\t// If this is a chained call of the form f(g()) where g has\n\t// multiple return values (MRV), they are flattened out into\n\t// args; a suffix of them may end up in a varargs slice.\n\tfor _, arg := range e.Args {\n\t\tv := b.expr(fn, arg)\n\t\tif ttuple, ok := v.Type().(*types.Tuple); ok { // MRV chain\n\t\t\tfor i, n := 0, ttuple.Len(); i < n; i++ {\n\t\t\t\targs = append(args, emitExtract(fn, v, i, arg))\n\t\t\t}\n\t\t} else {\n\t\t\targs = append(args, v)\n\t\t}\n\t}\n\n\t// Actual->formal assignability conversions for normal parameters.\n\tnp := sig.Params().Len() // number of normal parameters\n\tif sig.Variadic() {\n\t\tnp--\n\t}\n\tfor i := 0; i < np; i++ {\n\t\targs[offset+i] = emitConv(fn, args[offset+i], sig.Params().At(i).Type(), args[offset+i].Source())\n\t}\n\n\t// Actual->formal assignability conversions for variadic parameter,\n\t// and construction of slice.\n\tif sig.Variadic() {\n\t\tvarargs := args[offset+np:]\n\t\tst := sig.Params().At(np).Type().(*types.Slice)\n\t\tvt := st.Elem()\n\t\tif len(varargs) == 0 {\n\t\t\targs = append(args, emitConst(fn, nilConst(st, nil)))\n\t\t} else {\n\t\t\t// Replace a suffix of args with a slice containing it.\n\t\t\tat := types.NewArray(vt, int64(len(varargs)))\n\t\t\ta := emitNew(fn, at, e, \"varargs\")\n\t\t\ta.source = e\n\t\t\tfor i, arg := range varargs {\n\t\t\t\tiaddr := &IndexAddr{\n\t\t\t\t\tX:     a,\n\t\t\t\t\tIndex: emitConst(fn, intConst(int64(i), nil)),\n\t\t\t\t}\n\t\t\t\tiaddr.setType(types.NewPointer(vt))\n\t\t\t\tfn.emit(iaddr, e)\n\t\t\t\temitStore(fn, iaddr, arg, arg.Source())\n\t\t\t}\n\t\t\ts := &Slice{X: a}\n\t\t\ts.setType(st)\n\t\t\targs[offset+np] = fn.emit(s, args[offset+np].Source())\n\t\t\targs = args[:offset+np+1]\n\t\t}\n\t}\n\treturn args\n}\n\n// setCall emits to fn code to evaluate all the parameters of a function\n// call e, and populates *c with those values.\nfunc (b *builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) {\n\t// First deal with the f(...) part and optional receiver.\n\tb.setCallFunc(fn, e, c)\n\n\t// Then append the other actual parameters.\n\tsig, _ := typeutil.CoreType(fn.Pkg.typeOf(e.Fun)).(*types.Signature)\n\tif sig == nil {\n\t\tpanic(fmt.Sprintf(\"no signature for call of %s\", e.Fun))\n\t}\n\tc.Args = b.emitCallArgs(fn, sig, e, c.Args)\n}\n\n// assignOp emits to fn code to perform loc <op>= val.\nfunc (b *builder) assignOp(fn *Function, loc lvalue, val Value, op token.Token, source ast.Node) {\n\tloc.store(fn, emitArith(fn, op, loc.load(fn, source), val, loc.typ(), source), source)\n}\n\n// localValueSpec emits to fn code to define all of the vars in the\n// function-local ValueSpec, spec.\nfunc (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {\n\tswitch {\n\tcase len(spec.Values) == len(spec.Names):\n\t\t// e.g. var x, y = 0, 1\n\t\t// 1:1 assignment\n\t\tfor i, id := range spec.Names {\n\t\t\tif !isBlankIdent(id) {\n\t\t\t\temitLocalVar(fn, identVar(fn, id), id)\n\t\t\t}\n\t\t\tlval := b.addr(fn, id, false) // non-escaping\n\t\t\tb.assign(fn, lval, spec.Values[i], true, nil, spec)\n\t\t}\n\n\tcase len(spec.Values) == 0:\n\t\t// e.g. var x, y int\n\t\t// Locals are implicitly zero-initialized.\n\t\tfor _, id := range spec.Names {\n\t\t\tif !isBlankIdent(id) {\n\t\t\t\tlhs := emitLocalVar(fn, identVar(fn, id), id)\n\t\t\t\tif fn.debugInfo() {\n\t\t\t\t\temitDebugRef(fn, id, lhs, true)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tdefault:\n\t\t// e.g. var x, y = pos()\n\t\ttuple := b.exprN(fn, spec.Values[0])\n\t\tfor i, id := range spec.Names {\n\t\t\tif !isBlankIdent(id) {\n\t\t\t\temitLocalVar(fn, identVar(fn, id), id)\n\t\t\t\tlhs := b.addr(fn, id, false) // non-escaping\n\t\t\t\tlhs.store(fn, emitExtract(fn, tuple, i, id), id)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// assignStmt emits code to fn for a parallel assignment of rhss to lhss.\n// isDef is true if this is a short variable declaration (:=).\n//\n// Note the similarity with localValueSpec.\nfunc (b *builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool, source ast.Node) {\n\t// Side effects of all LHSs and RHSs must occur in left-to-right order.\n\tlvals := make([]lvalue, len(lhss))\n\tisZero := make([]bool, len(lhss))\n\tfor i, lhs := range lhss {\n\t\tvar lval lvalue = blank{}\n\t\tif !isBlankIdent(lhs) {\n\t\t\tif isDef {\n\t\t\t\tif obj, ok := fn.Pkg.info.Defs[lhs.(*ast.Ident)].(*types.Var); ok {\n\t\t\t\t\temitLocalVar(fn, obj, lhs)\n\t\t\t\t\tisZero[i] = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tlval = b.addr(fn, lhs, false) // non-escaping\n\t\t}\n\t\tlvals[i] = lval\n\t}\n\tif len(lhss) == len(rhss) {\n\t\t// Simple assignment:   x     = f()        (!isDef)\n\t\t// Parallel assignment: x, y  = f(), g()   (!isDef)\n\t\t// or short var decl:   x, y := f(), g()   (isDef)\n\t\t//\n\t\t// In all cases, the RHSs may refer to the LHSs,\n\t\t// so we need a storebuf.\n\t\tvar sb storebuf\n\t\tfor i := range rhss {\n\t\t\tb.assign(fn, lvals[i], rhss[i], isZero[i], &sb, source)\n\t\t}\n\t\tsb.emit(fn)\n\t} else {\n\t\t// e.g. x, y = pos()\n\t\ttuple := b.exprN(fn, rhss[0])\n\t\temitDebugRef(fn, rhss[0], tuple, false)\n\t\tfor i, lval := range lvals {\n\t\t\tlval.store(fn, emitExtract(fn, tuple, i, source), source)\n\t\t}\n\t}\n}\n\n// arrayLen returns the length of the array whose composite literal elements are elts.\nfunc (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {\n\tvar max int64 = -1\n\tvar i int64 = -1\n\tfor _, e := range elts {\n\t\tif kv, ok := e.(*ast.KeyValueExpr); ok {\n\t\t\ti = b.expr(fn, kv.Key).(*Const).Int64()\n\t\t} else {\n\t\t\ti++\n\t\t}\n\t\tif i > max {\n\t\t\tmax = i\n\t\t}\n\t}\n\treturn max + 1\n}\n\n// compLit emits to fn code to initialize a composite literal e at\n// address addr with type typ.\n//\n// Nested composite literals are recursively initialized in place\n// where possible. If isZero is true, compLit assumes that addr\n// holds the zero value for typ.\n//\n// Because the elements of a composite literal may refer to the\n// variables being updated, as in the second line below,\n//\n//\tx := T{a: 1}\n//\tx = T{a: x.a}\n//\n// all the reads must occur before all the writes. This is implicitly handled by the write buffering effected by\n// compositeElement and explicitly by the storebuf for when we don't use CompositeValue.\n//\n// A CompositeLit may have pointer type only in the recursive (nested)\n// case when the type name is implicit.  e.g. in []*T{{}}, the inner\n// literal has type *T behaves like &T{}.\n// In that case, addr must hold a T, not a *T.\nfunc (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) {\n\ttyp := deref(fn.Pkg.typeOf(e))\n\tswitch t := typeutil.CoreType(typ).(type) {\n\tcase *types.Struct:\n\t\tlvalue := &address{addr: addr, expr: e}\n\t\tif len(e.Elts) == 0 {\n\t\t\tif !isZero {\n\t\t\t\tsb.store(lvalue, zeroValue(fn, deref(addr.Type()), e), e)\n\t\t\t}\n\t\t} else {\n\t\t\tv := &CompositeValue{\n\t\t\t\tValues: make([]Value, t.NumFields()),\n\t\t\t}\n\t\t\tfor i := 0; i < t.NumFields(); i++ {\n\t\t\t\tv.Values[i] = emitConst(fn, zeroConst(t.Field(i).Type(), e))\n\t\t\t}\n\t\t\tv.setType(typ)\n\n\t\t\tfor i, e := range e.Elts {\n\t\t\t\tfieldIndex := i\n\t\t\t\tif kv, ok := e.(*ast.KeyValueExpr); ok {\n\t\t\t\t\tfname := kv.Key.(*ast.Ident).Name\n\t\t\t\t\tfor i, n := 0, t.NumFields(); i < n; i++ {\n\t\t\t\t\t\tsf := t.Field(i)\n\t\t\t\t\t\tif sf.Name() == fname {\n\t\t\t\t\t\t\tfieldIndex = i\n\t\t\t\t\t\t\te = kv.Value\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tce := &compositeElement{\n\t\t\t\t\tcv:   v,\n\t\t\t\t\tidx:  fieldIndex,\n\t\t\t\t\tt:    t.Field(fieldIndex).Type(),\n\t\t\t\t\texpr: e,\n\t\t\t\t}\n\t\t\t\tb.assign(fn, ce, e, isZero, sb, e)\n\t\t\t\tv.Bitmap.SetBit(&v.Bitmap, fieldIndex, 1)\n\t\t\t\tv.NumSet++\n\t\t\t}\n\t\t\tfn.emit(v, e)\n\t\t\tsb.store(lvalue, v, e)\n\t\t}\n\n\tcase *types.Array, *types.Slice:\n\t\tvar at *types.Array\n\t\tvar array Value\n\t\tswitch t := t.(type) {\n\t\tcase *types.Slice:\n\t\t\tat = types.NewArray(t.Elem(), b.arrayLen(fn, e.Elts))\n\t\t\tarray = emitNew(fn, at, e, \"slicelit\")\n\t\tcase *types.Array:\n\t\t\tat = t\n\t\t\tarray = addr\n\t\t}\n\n\t\tvar final Value\n\t\tif len(e.Elts) == 0 {\n\t\t\tif !isZero {\n\t\t\t\tzc := emitConst(fn, zeroConst(at, e))\n\t\t\t\tfinal = zc\n\t\t\t}\n\t\t} else {\n\t\t\tif at.Len() == int64(len(e.Elts)) {\n\t\t\t\t// The literal specifies all elements, so we can use a composite value\n\t\t\t\tv := &CompositeValue{\n\t\t\t\t\tValues: make([]Value, at.Len()),\n\t\t\t\t}\n\t\t\t\tzc := emitConst(fn, zeroConst(at.Elem(), e))\n\t\t\t\tfor i := range v.Values {\n\t\t\t\t\tv.Values[i] = zc\n\t\t\t\t}\n\t\t\t\tv.setType(at)\n\n\t\t\t\tvar idx *Const\n\t\t\t\tfor _, e := range e.Elts {\n\t\t\t\t\tif kv, ok := e.(*ast.KeyValueExpr); ok {\n\t\t\t\t\t\tidx = b.expr(fn, kv.Key).(*Const)\n\t\t\t\t\t\te = kv.Value\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar idxval int64\n\t\t\t\t\t\tif idx != nil {\n\t\t\t\t\t\t\tidxval = idx.Int64() + 1\n\t\t\t\t\t\t}\n\t\t\t\t\t\tidx = emitConst(fn, intConst(idxval, e)).(*Const)\n\t\t\t\t\t}\n\n\t\t\t\t\tiaddr := &compositeElement{\n\t\t\t\t\t\tcv:   v,\n\t\t\t\t\t\tidx:  int(idx.Int64()),\n\t\t\t\t\t\tt:    at.Elem(),\n\t\t\t\t\t\texpr: e,\n\t\t\t\t\t}\n\n\t\t\t\t\tb.assign(fn, iaddr, e, true, sb, e)\n\t\t\t\t\tv.Bitmap.SetBit(&v.Bitmap, int(idx.Int64()), 1)\n\t\t\t\t\tv.NumSet++\n\t\t\t\t}\n\t\t\t\tfinal = v\n\t\t\t\tfn.emit(v, e)\n\t\t\t} else {\n\t\t\t\t// Not all elements are specified. Populate the array with a series of stores, to guard against literals\n\t\t\t\t// like []int{1<<62: 1}.\n\t\t\t\tif !isZero {\n\t\t\t\t\t// memclear\n\t\t\t\t\tsb.store(&address{array, nil}, zeroValue(fn, deref(array.Type()), e), e)\n\t\t\t\t}\n\n\t\t\t\tvar idx *Const\n\t\t\t\tfor _, e := range e.Elts {\n\t\t\t\t\tif kv, ok := e.(*ast.KeyValueExpr); ok {\n\t\t\t\t\t\tidx = b.expr(fn, kv.Key).(*Const)\n\t\t\t\t\t\te = kv.Value\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar idxval int64\n\t\t\t\t\t\tif idx != nil {\n\t\t\t\t\t\t\tidxval = idx.Int64() + 1\n\t\t\t\t\t\t}\n\t\t\t\t\t\tidx = emitConst(fn, intConst(idxval, e)).(*Const)\n\t\t\t\t\t}\n\t\t\t\t\tiaddr := &IndexAddr{\n\t\t\t\t\t\tX:     array,\n\t\t\t\t\t\tIndex: idx,\n\t\t\t\t\t}\n\t\t\t\t\tiaddr.setType(types.NewPointer(at.Elem()))\n\t\t\t\t\tfn.emit(iaddr, e)\n\t\t\t\t\tif t != at { // slice\n\t\t\t\t\t\t// backing array is unaliased => storebuf not needed.\n\t\t\t\t\t\tb.assign(fn, &address{addr: iaddr, expr: e}, e, true, nil, e)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tb.assign(fn, &address{addr: iaddr, expr: e}, e, true, sb, e)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif t != at { // slice\n\t\t\tif final != nil {\n\t\t\t\tsb.store(&address{addr: array}, final, e)\n\t\t\t}\n\t\t\ts := &Slice{X: array}\n\t\t\ts.setType(typ)\n\t\t\tsb.store(&address{addr: addr, expr: e}, fn.emit(s, e), e)\n\t\t} else if final != nil {\n\t\t\tsb.store(&address{addr: array, expr: e}, final, e)\n\t\t}\n\n\tcase *types.Map:\n\t\tm := &MakeMap{Reserve: emitConst(fn, intConst(int64(len(e.Elts)), e))}\n\t\tm.setType(typ)\n\t\tfn.emit(m, e)\n\t\tfor _, e := range e.Elts {\n\t\t\te := e.(*ast.KeyValueExpr)\n\n\t\t\t// If a key expression in a map literal is  itself a\n\t\t\t// composite literal, the type may be omitted.\n\t\t\t// For example:\n\t\t\t//\tmap[*struct{}]bool{{}: true}\n\t\t\t// An &-operation may be implied:\n\t\t\t//\tmap[*struct{}]bool{&struct{}{}: true}\n\t\t\tvar key Value\n\t\t\tif _, ok := unparen(e.Key).(*ast.CompositeLit); ok && isPointer(t.Key()) {\n\t\t\t\t// A CompositeLit never evaluates to a pointer,\n\t\t\t\t// so if the type of the location is a pointer,\n\t\t\t\t// an &-operation is implied.\n\t\t\t\tkey = b.addr(fn, e.Key, true).address(fn)\n\t\t\t} else {\n\t\t\t\tkey = b.expr(fn, e.Key)\n\t\t\t}\n\n\t\t\tloc := element{\n\t\t\t\tm: m,\n\t\t\t\tk: emitConv(fn, key, t.Key(), e),\n\t\t\t\tt: t.Elem(),\n\t\t\t}\n\n\t\t\t// We call assign() only because it takes care\n\t\t\t// of any &-operation required in the recursive\n\t\t\t// case, e.g.,\n\t\t\t// map[int]*struct{}{0: {}} implies &struct{}{}.\n\t\t\t// In-place update is of course impossible,\n\t\t\t// and no storebuf is needed.\n\t\t\tb.assign(fn, &loc, e.Value, true, nil, e)\n\t\t}\n\t\tsb.store(&address{addr: addr, expr: e}, m, e)\n\n\tdefault:\n\t\tpanic(\"unexpected CompositeLit type: \" + t.String())\n\t}\n}\n\nfunc (b *builder) switchStmt(fn *Function, s *ast.SwitchStmt, label *lblock) {\n\tif s.Tag == nil {\n\t\tb.switchStmtDynamic(fn, s, label)\n\t\treturn\n\t}\n\tdynamic := false\n\tfor _, iclause := range s.Body.List {\n\t\tclause := iclause.(*ast.CaseClause)\n\t\tfor _, cond := range clause.List {\n\t\t\tif fn.Pkg.info.Types[unparen(cond)].Value == nil {\n\t\t\t\tdynamic = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tif dynamic {\n\t\tb.switchStmtDynamic(fn, s, label)\n\t\treturn\n\t}\n\n\tif s.Init != nil {\n\t\tb.stmt(fn, s.Init)\n\t}\n\n\tentry := fn.currentBlock\n\ttag := b.expr(fn, s.Tag)\n\n\theads := make([]*BasicBlock, 0, len(s.Body.List))\n\tbodies := make([]*BasicBlock, len(s.Body.List))\n\tconds := make([]Value, 0, len(s.Body.List))\n\n\thasDefault := false\n\tdone := fn.newBasicBlock(\"switch.done\")\n\tif label != nil {\n\t\tlabel._break = done\n\t}\n\tfor i, stmt := range s.Body.List {\n\t\tbody := fn.newBasicBlock(fmt.Sprintf(\"switch.body.%d\", i))\n\t\tbodies[i] = body\n\t\tcas := stmt.(*ast.CaseClause)\n\t\tif cas.List == nil {\n\t\t\t// default branch\n\t\t\thasDefault = true\n\t\t\thead := fn.newBasicBlock(fmt.Sprintf(\"switch.head.%d\", i))\n\t\t\tconds = append(conds, nil)\n\t\t\theads = append(heads, head)\n\t\t\tfn.currentBlock = head\n\t\t\temitJump(fn, body, cas)\n\t\t}\n\t\tfor j, cond := range stmt.(*ast.CaseClause).List {\n\t\t\tfn.currentBlock = entry\n\t\t\thead := fn.newBasicBlock(fmt.Sprintf(\"switch.head.%d.%d\", i, j))\n\t\t\tconds = append(conds, b.expr(fn, cond))\n\t\t\theads = append(heads, head)\n\t\t\tfn.currentBlock = head\n\t\t\temitJump(fn, body, cond)\n\t\t}\n\t}\n\n\tfor i, stmt := range s.Body.List {\n\t\tclause := stmt.(*ast.CaseClause)\n\t\tbody := bodies[i]\n\t\tfn.currentBlock = body\n\t\tfallthru := done\n\t\tif i+1 < len(bodies) {\n\t\t\tfallthru = bodies[i+1]\n\t\t}\n\t\tfn.targets = &targets{\n\t\t\ttail:         fn.targets,\n\t\t\t_break:       done,\n\t\t\t_fallthrough: fallthru,\n\t\t}\n\t\tb.stmtList(fn, clause.Body)\n\t\tfn.targets = fn.targets.tail\n\t\temitJump(fn, done, stmt)\n\t}\n\n\tif !hasDefault {\n\t\thead := fn.newBasicBlock(\"switch.head.implicit-default\")\n\t\tbody := fn.newBasicBlock(\"switch.body.implicit-default\")\n\t\tfn.currentBlock = head\n\t\temitJump(fn, body, s)\n\t\tfn.currentBlock = body\n\t\temitJump(fn, done, s)\n\t\theads = append(heads, head)\n\t\tconds = append(conds, nil)\n\t}\n\n\tif len(heads) != len(conds) {\n\t\tpanic(fmt.Sprintf(\"internal error: %d heads for %d conds\", len(heads), len(conds)))\n\t}\n\tfor _, head := range heads {\n\t\taddEdge(entry, head)\n\t}\n\tfn.currentBlock = entry\n\tentry.emit(&ConstantSwitch{\n\t\tTag:   tag,\n\t\tConds: conds,\n\t}, s)\n\tfn.currentBlock = done\n}\n\n// switchStmt emits to fn code for the switch statement s, optionally\n// labelled by label.\nfunc (b *builder) switchStmtDynamic(fn *Function, s *ast.SwitchStmt, label *lblock) {\n\t// We treat SwitchStmt like a sequential if-else chain.\n\t// Multiway dispatch can be recovered later by irutil.Switches()\n\t// to those cases that are free of side effects.\n\tif s.Init != nil {\n\t\tb.stmt(fn, s.Init)\n\t}\n\tkTrue := emitConst(fn, NewConst(constant.MakeBool(true), tBool, nil))\n\n\tvar tagv Value = kTrue\n\tvar tagSource ast.Node = s\n\tif s.Tag != nil {\n\t\ttagv = b.expr(fn, s.Tag)\n\t\ttagSource = s.Tag\n\t}\n\t// lifting only considers loads and stores, but we want different\n\t// sigma nodes for the different comparisons. use a temporary and\n\t// load it in every branch.\n\ttag := emitLocal(fn, tagv.Type(), tagSource, \"switch.value\")\n\ttag.comment = \"switch.tag\"\n\temitStore(fn, tag, tagv, tagSource)\n\n\tdone := fn.newBasicBlock(\"switch.done\")\n\tif label != nil {\n\t\tlabel._break = done\n\t}\n\t// We pull the default case (if present) down to the end.\n\t// But each fallthrough label must point to the next\n\t// body block in source order, so we preallocate a\n\t// body block (fallthru) for the next case.\n\t// Unfortunately this makes for a confusing block order.\n\tvar dfltBody *[]ast.Stmt\n\tvar dfltFallthrough *BasicBlock\n\tvar fallthru, dfltBlock *BasicBlock\n\tncases := len(s.Body.List)\n\tfor i, clause := range s.Body.List {\n\t\tbody := fallthru\n\t\tif body == nil {\n\t\t\tbody = fn.newBasicBlock(\"switch.body\") // first case only\n\t\t}\n\n\t\t// Preallocate body block for the next case.\n\t\tfallthru = done\n\t\tif i+1 < ncases {\n\t\t\tfallthru = fn.newBasicBlock(\"switch.body\")\n\t\t}\n\n\t\tcc := clause.(*ast.CaseClause)\n\t\tif cc.List == nil {\n\t\t\t// Default case.\n\t\t\tdfltBody = &cc.Body\n\t\t\tdfltFallthrough = fallthru\n\t\t\tdfltBlock = body\n\t\t\tcontinue\n\t\t}\n\n\t\tvar nextCond *BasicBlock\n\t\tfor _, cond := range cc.List {\n\t\t\tnextCond = fn.newBasicBlock(\"switch.next\")\n\t\t\tif tagv == kTrue {\n\t\t\t\t// emit a proper if/else chain instead of a comparison\n\t\t\t\t// of a value against true.\n\t\t\t\t//\n\t\t\t\t// NOTE(dh): adonovan had a todo saying \"don't forget\n\t\t\t\t// conversions though\". As far as I can tell, there\n\t\t\t\t// aren't any conversions that we need to take care of\n\t\t\t\t// here. `case bool(a) && bool(b)` as well as `case\n\t\t\t\t// bool(a && b)` are being taken care of by b.cond,\n\t\t\t\t// and `case a` where a is not of type bool is\n\t\t\t\t// invalid.\n\t\t\t\tb.cond(fn, cond, body, nextCond)\n\t\t\t} else {\n\t\t\t\tcond := emitCompare(fn, token.EQL, emitLoad(fn, tag, cond), b.expr(fn, cond), cond)\n\t\t\t\temitIf(fn, cond, body, nextCond, cond.Source())\n\t\t\t}\n\n\t\t\tfn.currentBlock = nextCond\n\t\t}\n\t\tfn.currentBlock = body\n\t\tfn.targets = &targets{\n\t\t\ttail:         fn.targets,\n\t\t\t_break:       done,\n\t\t\t_fallthrough: fallthru,\n\t\t}\n\t\tb.stmtList(fn, cc.Body)\n\t\tfn.targets = fn.targets.tail\n\t\temitJump(fn, done, s)\n\t\tfn.currentBlock = nextCond\n\t}\n\tif dfltBlock != nil {\n\t\t// The lack of a Source for the jump doesn't matter, block\n\t\t// fusing will get rid of the jump later.\n\n\t\temitJump(fn, dfltBlock, s)\n\t\tfn.currentBlock = dfltBlock\n\t\tfn.targets = &targets{\n\t\t\ttail:         fn.targets,\n\t\t\t_break:       done,\n\t\t\t_fallthrough: dfltFallthrough,\n\t\t}\n\t\tb.stmtList(fn, *dfltBody)\n\t\tfn.targets = fn.targets.tail\n\t}\n\temitJump(fn, done, s)\n\tfn.currentBlock = done\n}\n\nfunc (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lblock) {\n\tif s.Init != nil {\n\t\tb.stmt(fn, s.Init)\n\t}\n\n\tvar tag Value\n\tswitch e := s.Assign.(type) {\n\tcase *ast.ExprStmt: // x.(type)\n\t\ttag = b.expr(fn, unparen(e.X).(*ast.TypeAssertExpr).X)\n\tcase *ast.AssignStmt: // y := x.(type)\n\t\ttag = b.expr(fn, unparen(e.Rhs[0]).(*ast.TypeAssertExpr).X)\n\tdefault:\n\t\tpanic(\"unreachable\")\n\t}\n\ttagPtr := emitLocal(fn, tag.Type(), tag.Source(), \"\")\n\temitStore(fn, tagPtr, tag, tag.Source())\n\n\t// +1 in case there's no explicit default case\n\theads := make([]*BasicBlock, 0, len(s.Body.List)+1)\n\n\tentry := fn.currentBlock\n\tdone := fn.newBasicBlock(\"done\")\n\tif label != nil {\n\t\tlabel._break = done\n\t}\n\n\t// set up type switch and constant switch, populate their conditions\n\ttswtch := &TypeSwitch{\n\t\tTag:   emitLoad(fn, tagPtr, tag.Source()),\n\t\tConds: make([]types.Type, 0, len(s.Body.List)+1),\n\t}\n\tcswtch := &ConstantSwitch{\n\t\tConds: make([]Value, 0, len(s.Body.List)+1),\n\t}\n\n\trets := make([]types.Type, 0, len(s.Body.List)+1)\n\tindex := 0\n\tvar default_ *ast.CaseClause\n\tfor _, clause := range s.Body.List {\n\t\tcc := clause.(*ast.CaseClause)\n\t\tif obj, ok := fn.Pkg.info.Implicits[cc].(*types.Var); ok {\n\t\t\temitLocalVar(fn, obj, cc)\n\t\t}\n\t\tif cc.List == nil {\n\t\t\t// default case\n\t\t\tdefault_ = cc\n\t\t} else {\n\t\t\tfor _, expr := range cc.List {\n\t\t\t\ttswtch.Conds = append(tswtch.Conds, fn.Pkg.typeOf(expr))\n\t\t\t\tcswtch.Conds = append(cswtch.Conds, emitConst(fn, intConst(int64(index), expr)))\n\t\t\t\tindex++\n\t\t\t}\n\t\t\tif len(cc.List) == 1 {\n\t\t\t\trets = append(rets, fn.Pkg.typeOf(cc.List[0]))\n\t\t\t} else {\n\t\t\t\tfor range cc.List {\n\t\t\t\t\trets = append(rets, tag.Type())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// default branch\n\trets = append(rets, tag.Type())\n\n\tvar vars []*types.Var\n\tvars = append(vars, varIndex)\n\tfor _, typ := range rets {\n\t\tvars = append(vars, anonVar(typ))\n\t}\n\ttswtch.setType(types.NewTuple(vars...))\n\t// default branch\n\tfn.currentBlock = entry\n\tfn.emit(tswtch, s)\n\tcswtch.Conds = append(cswtch.Conds, emitConst(fn, intConst(int64(-1), nil)))\n\t// in theory we should add a local and stores/loads for tswtch, to\n\t// generate sigma nodes in the branches. however, there isn't any\n\t// useful information we could possibly attach to it.\n\tcswtch.Tag = emitExtract(fn, tswtch, 0, s)\n\tfn.emit(cswtch, s)\n\n\t// build heads and bodies\n\tindex = 0\n\tfor _, clause := range s.Body.List {\n\t\tcc := clause.(*ast.CaseClause)\n\t\tif cc.List == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tbody := fn.newBasicBlock(\"typeswitch.body\")\n\t\tfor _, expr := range cc.List {\n\t\t\thead := fn.newBasicBlock(\"typeswitch.head\")\n\t\t\theads = append(heads, head)\n\t\t\tfn.currentBlock = head\n\n\t\t\tif obj, ok := fn.Pkg.info.Implicits[cc].(*types.Var); ok {\n\t\t\t\t// In a switch y := x.(type), each case clause\n\t\t\t\t// implicitly declares a distinct object y.\n\t\t\t\t// In a single-type case, y has that type.\n\t\t\t\t// In multi-type cases, 'case nil' and default,\n\t\t\t\t// y has the same type as the interface operand.\n\n\t\t\t\tl := fn.vars[obj]\n\t\t\t\tif rets[index] == tUntypedNil {\n\t\t\t\t\temitStore(fn, l, emitConst(fn, nilConst(tswtch.Tag.Type(), nil)), s.Assign)\n\t\t\t\t} else {\n\t\t\t\t\tx := emitExtract(fn, tswtch, index+1, s.Assign)\n\t\t\t\t\temitStore(fn, l, x, nil)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\temitJump(fn, body, expr)\n\t\t\tindex++\n\t\t}\n\t\tfn.currentBlock = body\n\t\tfn.targets = &targets{\n\t\t\ttail:   fn.targets,\n\t\t\t_break: done,\n\t\t}\n\t\tb.stmtList(fn, cc.Body)\n\t\tfn.targets = fn.targets.tail\n\t\temitJump(fn, done, clause)\n\t}\n\n\tif default_ == nil {\n\t\t// implicit default\n\t\theads = append(heads, done)\n\t} else {\n\t\tbody := fn.newBasicBlock(\"typeswitch.default\")\n\t\theads = append(heads, body)\n\t\tfn.currentBlock = body\n\t\tfn.targets = &targets{\n\t\t\ttail:   fn.targets,\n\t\t\t_break: done,\n\t\t}\n\t\tif obj, ok := fn.Pkg.info.Implicits[default_].(*types.Var); ok {\n\t\t\tl := fn.vars[obj]\n\t\t\tx := emitExtract(fn, tswtch, index+1, s.Assign)\n\t\t\temitStore(fn, l, x, s)\n\t\t}\n\t\tb.stmtList(fn, default_.Body)\n\t\tfn.targets = fn.targets.tail\n\t\temitJump(fn, done, s)\n\t}\n\n\tfn.currentBlock = entry\n\tfor _, head := range heads {\n\t\taddEdge(entry, head)\n\t}\n\tfn.currentBlock = done\n}\n\n// selectStmt emits to fn code for the select statement s, optionally\n// labelled by label.\nfunc (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (noreturn bool) {\n\tif len(s.Body.List) == 0 {\n\t\tinstr := &Select{Blocking: true}\n\t\tinstr.setType(types.NewTuple(varIndex, varOk))\n\t\tfn.emit(instr, s)\n\t\tfn.emit(new(Unreachable), s)\n\t\taddEdge(fn.currentBlock, fn.Exit)\n\t\treturn true\n\t}\n\n\t// A blocking select of a single case degenerates to a\n\t// simple send or receive.\n\t// TODO(adonovan): opt: is this optimization worth its weight?\n\tif len(s.Body.List) == 1 {\n\t\tclause := s.Body.List[0].(*ast.CommClause)\n\t\tif clause.Comm != nil {\n\t\t\tb.stmt(fn, clause.Comm)\n\t\t\tdone := fn.newBasicBlock(\"select.done\")\n\t\t\tif label != nil {\n\t\t\t\tlabel._break = done\n\t\t\t}\n\t\t\tfn.targets = &targets{\n\t\t\t\ttail:   fn.targets,\n\t\t\t\t_break: done,\n\t\t\t}\n\t\t\tb.stmtList(fn, clause.Body)\n\t\t\tfn.targets = fn.targets.tail\n\t\t\temitJump(fn, done, clause)\n\t\t\tfn.currentBlock = done\n\t\t\treturn false\n\t\t}\n\t}\n\n\t// First evaluate all channels in all cases, and find\n\t// the directions of each state.\n\tvar states []*SelectState\n\tblocking := true\n\tdebugInfo := fn.debugInfo()\n\tfor _, clause := range s.Body.List {\n\t\tvar st *SelectState\n\t\tswitch comm := clause.(*ast.CommClause).Comm.(type) {\n\t\tcase nil: // default case\n\t\t\tblocking = false\n\t\t\tcontinue\n\n\t\tcase *ast.SendStmt: // ch<- i\n\t\t\tch := b.expr(fn, comm.Chan)\n\t\t\tst = &SelectState{\n\t\t\t\tDir:  types.SendOnly,\n\t\t\t\tChan: ch,\n\t\t\t\tSend: emitConv(fn, b.expr(fn, comm.Value),\n\t\t\t\t\ttypeutil.CoreType(ch.Type()).Underlying().(*types.Chan).Elem(), comm),\n\t\t\t\tPos: comm.Arrow,\n\t\t\t}\n\t\t\tif debugInfo {\n\t\t\t\tst.DebugNode = comm\n\t\t\t}\n\n\t\tcase *ast.AssignStmt: // x := <-ch\n\t\t\trecv := unparen(comm.Rhs[0]).(*ast.UnaryExpr)\n\t\t\tst = &SelectState{\n\t\t\t\tDir:  types.RecvOnly,\n\t\t\t\tChan: b.expr(fn, recv.X),\n\t\t\t\tPos:  recv.OpPos,\n\t\t\t}\n\t\t\tif debugInfo {\n\t\t\t\tst.DebugNode = recv\n\t\t\t}\n\n\t\tcase *ast.ExprStmt: // <-ch\n\t\t\trecv := unparen(comm.X).(*ast.UnaryExpr)\n\t\t\tst = &SelectState{\n\t\t\t\tDir:  types.RecvOnly,\n\t\t\t\tChan: b.expr(fn, recv.X),\n\t\t\t\tPos:  recv.OpPos,\n\t\t\t}\n\t\t\tif debugInfo {\n\t\t\t\tst.DebugNode = recv\n\t\t\t}\n\t\t}\n\t\tstates = append(states, st)\n\t}\n\n\t// We dispatch on the (fair) result of Select using a\n\t// switch on the returned index.\n\tsel := &Select{\n\t\tStates:   states,\n\t\tBlocking: blocking,\n\t}\n\tsel.source = s\n\tvar vars []*types.Var\n\tvars = append(vars, varIndex, varOk)\n\tfor _, st := range states {\n\t\tif st.Dir == types.RecvOnly {\n\t\t\ttElem := typeutil.CoreType(st.Chan.Type()).Underlying().(*types.Chan).Elem()\n\t\t\tvars = append(vars, anonVar(tElem))\n\t\t}\n\t}\n\tsel.setType(types.NewTuple(vars...))\n\tfn.emit(sel, s)\n\tidx := emitExtract(fn, sel, 0, s)\n\n\tdone := fn.newBasicBlock(\"select.done\")\n\tif label != nil {\n\t\tlabel._break = done\n\t}\n\n\tentry := fn.currentBlock\n\tswtch := &ConstantSwitch{\n\t\tTag: idx,\n\t\t// one condition per case\n\t\tConds: make([]Value, 0, len(s.Body.List)+1),\n\t}\n\t// note that we don't need heads; a select case can only have a single condition\n\tvar bodies []*BasicBlock\n\n\tstate := 0\n\tr := 2 // index in 'sel' tuple of value; increments if st.Dir==RECV\n\tfor _, cc := range s.Body.List {\n\t\tclause := cc.(*ast.CommClause)\n\t\tif clause.Comm == nil {\n\t\t\tbody := fn.newBasicBlock(\"select.default\")\n\t\t\tfn.currentBlock = body\n\t\t\tbodies = append(bodies, body)\n\t\t\tfn.targets = &targets{\n\t\t\t\ttail:   fn.targets,\n\t\t\t\t_break: done,\n\t\t\t}\n\t\t\tb.stmtList(fn, clause.Body)\n\t\t\temitJump(fn, done, s)\n\t\t\tfn.targets = fn.targets.tail\n\t\t\tswtch.Conds = append(swtch.Conds, emitConst(fn, intConst(-1, nil)))\n\t\t\tcontinue\n\t\t}\n\t\tswtch.Conds = append(swtch.Conds, emitConst(fn, intConst(int64(state), nil)))\n\t\tbody := fn.newBasicBlock(\"select.body\")\n\t\tfn.currentBlock = body\n\t\tbodies = append(bodies, body)\n\t\tfn.targets = &targets{\n\t\t\ttail:   fn.targets,\n\t\t\t_break: done,\n\t\t}\n\t\tswitch comm := clause.Comm.(type) {\n\t\tcase *ast.ExprStmt: // <-ch\n\t\t\tif debugInfo {\n\t\t\t\tv := emitExtract(fn, sel, r, comm)\n\t\t\t\temitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)\n\t\t\t}\n\t\t\tr++\n\n\t\tcase *ast.AssignStmt: // x := <-states[state].Chan\n\t\t\tif comm.Tok == token.DEFINE {\n\t\t\t\tid := comm.Lhs[0].(*ast.Ident)\n\t\t\t\temitLocalVar(fn, identVar(fn, id), id)\n\t\t\t}\n\t\t\tx := b.addr(fn, comm.Lhs[0], false) // non-escaping\n\t\t\tv := emitExtract(fn, sel, r, comm)\n\t\t\tif debugInfo {\n\t\t\t\temitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)\n\t\t\t}\n\t\t\tx.store(fn, v, comm)\n\n\t\t\tif len(comm.Lhs) == 2 { // x, ok := ...\n\t\t\t\tif comm.Tok == token.DEFINE {\n\t\t\t\t\tid := comm.Lhs[1].(*ast.Ident)\n\t\t\t\t\temitLocalVar(fn, identVar(fn, id), id)\n\t\t\t\t}\n\t\t\t\tok := b.addr(fn, comm.Lhs[1], false) // non-escaping\n\t\t\t\tok.store(fn, emitExtract(fn, sel, 1, comm), comm)\n\t\t\t}\n\t\t\tr++\n\t\t}\n\t\tb.stmtList(fn, clause.Body)\n\t\tfn.targets = fn.targets.tail\n\t\temitJump(fn, done, s)\n\t\tstate++\n\t}\n\tfn.currentBlock = entry\n\tfn.emit(swtch, s)\n\tfor _, body := range bodies {\n\t\taddEdge(entry, body)\n\t}\n\tfn.currentBlock = done\n\treturn false\n}\n\n// forStmt emits to fn code for the for statement s, optionally\n// labelled by label.\nfunc (b *builder) forStmt(fn *Function, s *ast.ForStmt, label *lblock) {\n\t// Use forStmtGo122 instead if it applies.\n\tif s.Init != nil {\n\t\tif assign, ok := s.Init.(*ast.AssignStmt); ok && assign.Tok == token.DEFINE {\n\t\t\tif version.Compare(fn.goversion, \"go1.22\") >= 0 {\n\t\t\t\tb.forStmtGo122(fn, s, label)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\t//\t...init...\n\t//      jump loop\n\t// loop:\n\t//      if cond goto body else done\n\t// body:\n\t//      ...body...\n\t//      jump post\n\t// post:\t\t\t\t (target of continue)\n\t//      ...post...\n\t//      jump loop\n\t// done:                                 (target of break)\n\tif s.Init != nil {\n\t\tb.stmt(fn, s.Init)\n\t}\n\tbody := fn.newBasicBlock(\"for.body\")\n\tdone := fn.newBasicBlock(\"for.done\") // target of 'break'\n\tloop := body                         // target of back-edge\n\tif s.Cond != nil {\n\t\tloop = fn.newBasicBlock(\"for.loop\")\n\t}\n\tcont := loop // target of 'continue'\n\tif s.Post != nil {\n\t\tcont = fn.newBasicBlock(\"for.post\")\n\t}\n\tif label != nil {\n\t\tlabel._break = done\n\t\tlabel._continue = cont\n\t}\n\temitJump(fn, loop, s)\n\tfn.currentBlock = loop\n\tif loop != body {\n\t\tb.cond(fn, s.Cond, body, done)\n\t\tfn.currentBlock = body\n\t}\n\tfn.targets = &targets{\n\t\ttail:      fn.targets,\n\t\t_break:    done,\n\t\t_continue: cont,\n\t}\n\tb.stmt(fn, s.Body)\n\tfn.targets = fn.targets.tail\n\temitJump(fn, cont, s)\n\n\tif s.Post != nil {\n\t\tfn.currentBlock = cont\n\t\tb.stmt(fn, s.Post)\n\t\temitJump(fn, loop, s) // back-edge\n\t}\n\tfn.currentBlock = done\n}\n\n// forStmtGo122 emits to fn code for the for statement s, optionally\n// labelled by label. s must define its variables.\n//\n// This allocates once per loop iteration. This is only correct in\n// GoVersions >= go1.22.\nfunc (b *builder) forStmtGo122(fn *Function, s *ast.ForStmt, label *lblock) {\n\t//     i_outer = alloc[T]\n\t//     *i_outer = ...init...        // under objects[i] = i_outer\n\t//     jump loop\n\t// loop:\n\t//     i = phi [head: i_outer, loop: i_next]\n\t//     ...cond...                   // under objects[i] = i\n\t//     if cond goto body else done\n\t// body:\n\t//     ...body...                   // under objects[i] = i (same as loop)\n\t//     jump post\n\t// post:\n\t//     tmp = *i\n\t//     i_next = alloc[T]\n\t//     *i_next = tmp\n\t//     ...post...                   // under objects[i] = i_next\n\t//     goto loop\n\t// done:\n\n\tinit := s.Init.(*ast.AssignStmt)\n\tstartingBlocks := len(fn.Blocks)\n\n\tpre := fn.currentBlock               // current block before starting\n\tloop := fn.newBasicBlock(\"for.loop\") // target of back-edge\n\tbody := fn.newBasicBlock(\"for.body\")\n\tpost := fn.newBasicBlock(\"for.post\") // target of 'continue'\n\tdone := fn.newBasicBlock(\"for.done\") // target of 'break'\n\n\t// For each of the n loop variables, we create five SSA values,\n\t// outer, phi, next, load, and store in pre, loop, and post.\n\t// There is no limit on n.\n\ttype loopVar struct {\n\t\tobj   *types.Var\n\t\touter *Alloc\n\t\tphi   *Phi\n\t\tload  *Load\n\t\tnext  *Alloc\n\t\tstore *Store\n\t}\n\tvars := make([]loopVar, len(init.Lhs))\n\tfor i, lhs := range init.Lhs {\n\t\tv := identVar(fn, lhs.(*ast.Ident))\n\n\t\tfn.currentBlock = pre\n\t\touter := emitLocal(fn, v.Type(), lhs, v.Name())\n\n\t\tfn.currentBlock = loop\n\t\tphi := &Phi{}\n\t\tphi.comment = v.Name()\n\t\tphi.typ = outer.Type()\n\t\tfn.emit(phi, lhs)\n\n\t\tfn.currentBlock = post\n\t\t// If next is local, it reuses the address and zeroes the old value so\n\t\t// load before allocating next.\n\t\tload := emitLoad(fn, phi, init)\n\t\tnext := emitLocal(fn, v.Type(), lhs, v.Name())\n\t\tstore := emitStore(fn, next, load, s)\n\n\t\tphi.Edges = []Value{outer, next} // pre edge is emitted before post edge.\n\t\tvars[i] = loopVar{v, outer, phi, load, next, store}\n\t}\n\n\t// ...init... under fn.objects[v] = i_outer\n\tfn.currentBlock = pre\n\tfor _, v := range vars {\n\t\tfn.vars[v.obj] = v.outer\n\t}\n\tconst isDef = false // assign to already-allocated outers\n\tb.assignStmt(fn, init.Lhs, init.Rhs, isDef, s)\n\tif label != nil {\n\t\tlabel._break = done\n\t\tlabel._continue = post\n\t}\n\temitJump(fn, loop, s)\n\n\t// ...cond... under fn.objects[v] = i\n\tfn.currentBlock = loop\n\tfor _, v := range vars {\n\t\tfn.vars[v.obj] = v.phi\n\t}\n\tif s.Cond != nil {\n\t\tb.cond(fn, s.Cond, body, done)\n\t} else {\n\t\temitJump(fn, body, s)\n\t}\n\n\t// ...body... under fn.objects[v] = i\n\tfn.currentBlock = body\n\tfn.targets = &targets{\n\t\ttail:      fn.targets,\n\t\t_break:    done,\n\t\t_continue: post,\n\t}\n\tb.stmt(fn, s.Body)\n\tfn.targets = fn.targets.tail\n\temitJump(fn, post, s)\n\n\t// ...post... under fn.objects[v] = i_next\n\tfor _, v := range vars {\n\t\tfn.vars[v.obj] = v.next\n\t}\n\tfn.currentBlock = post\n\tif s.Post != nil {\n\t\tb.stmt(fn, s.Post)\n\t}\n\temitJump(fn, loop, s) // back-edge\n\tfn.currentBlock = done\n\n\t// For each loop variable that does not escape,\n\t// (the common case), fuse its next cells into its\n\t// (local) outer cell as they have disjoint live ranges.\n\t//\n\t// It is sufficient to test whether i_next escapes,\n\t// because its Heap flag will be marked true if either\n\t// the cond or post expression causes i to escape\n\t// (because escape distributes over phi).\n\tvar nlocals int\n\tfor _, v := range vars {\n\t\tif !v.next.Heap {\n\t\t\tnlocals++\n\t\t}\n\t}\n\tif nlocals > 0 {\n\t\treplace := make(map[Value]Value, 2*nlocals)\n\t\tdead := make(map[Instruction]bool, 4*nlocals)\n\t\tfor _, v := range vars {\n\t\t\tif !v.next.Heap {\n\t\t\t\treplace[v.next] = v.outer\n\t\t\t\treplace[v.phi] = v.outer\n\t\t\t\tdead[v.phi], dead[v.next], dead[v.load], dead[v.store] = true, true, true, true\n\t\t\t}\n\t\t}\n\n\t\t// Replace all uses of i_next and phi with i_outer.\n\t\t// Referrers have not been built for fn yet so only update Instruction operands.\n\t\t// We need only look within the blocks added by the loop.\n\t\tvar operands []*Value // recycle storage\n\t\tfor _, b := range fn.Blocks[startingBlocks:] {\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\toperands = instr.Operands(operands[:0])\n\t\t\t\tfor _, ptr := range operands {\n\t\t\t\t\tk := *ptr\n\t\t\t\t\tif v := replace[k]; v != nil {\n\t\t\t\t\t\t*ptr = v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Remove instructions for phi, load, and store.\n\t\t// lift() will remove the unused i_next *Alloc.\n\t\tisDead := func(i Instruction) bool { return dead[i] }\n\t\tloop.Instrs = removeInstrsIf(loop.Instrs, isDead)\n\t\tpost.Instrs = removeInstrsIf(post.Instrs, isDead)\n\t}\n}\n\n// rangeIndexed emits to fn the header for an integer-indexed loop\n// over array, *array or slice value x.\n// The v result is defined only if tv is non-nil.\n// forPos is the position of the \"for\" token.\nfunc (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, source ast.Node) (k, v Value, loop, done *BasicBlock) {\n\t//\n\t//      length = len(x)\n\t//      index = -1\n\t// loop:                                   (target of continue)\n\t//      index++\n\t// \tif index < length goto body else done\n\t// body:\n\t//      k = index\n\t//      v = x[index]\n\t//      ...body...\n\t// \tjump loop\n\t// done:                                   (target of break)\n\n\t// We store in an Alloc and load it on each iteration so that lifting produces the necessary σ nodes\n\txAlloc := newVariable(fn, x.Type(), source)\n\txAlloc.store(x)\n\n\t// Determine number of iterations.\n\t//\n\t// We store the length in an Alloc and load it on each iteration so that lifting produces the necessary σ nodes\n\tlength := newVariable(fn, tInt, source)\n\tif arr, ok := typeutil.CoreType(deref(x.Type())).(*types.Array); ok {\n\t\t// For array or *array, the number of iterations is known statically thanks to the type. We avoid a data\n\t\t// dependence upon x, permitting later dead-code elimination if x is pure, static unrolling, etc. Ranging over a\n\t\t// nil *array may have >0 iterations. We still generate code for x, in case it has effects.\n\t\t//\n\t\t// We use the core type of x, even though the length of type parameters isn't constant as per the language\n\t\t// specification. Just because len(x) isn't constant doesn't mean we can't emit IR that takes advantage of a\n\t\t// known length.\n\t\tlength.store(emitConst(fn, intConst(arr.Len(), nil)))\n\t} else {\n\t\t// length = len(x).\n\t\tvar c Call\n\t\tc.Call.Value = makeLen(x.Type())\n\t\tc.Call.Args = []Value{x}\n\t\tc.setType(tInt)\n\t\tlength.store(fn.emit(&c, source))\n\t}\n\n\tindex := emitLocal(fn, tInt, source, \"rangeindex\")\n\temitStore(fn, index, emitConst(fn, intConst(-1, nil)), source)\n\n\tloop = fn.newBasicBlock(\"rangeindex.loop\")\n\temitJump(fn, loop, source)\n\tfn.currentBlock = loop\n\n\tincr := &BinOp{\n\t\tOp: token.ADD,\n\t\tX:  emitLoad(fn, index, source),\n\t\tY:  emitConst(fn, intConst(1, nil)),\n\t}\n\tincr.setType(tInt)\n\temitStore(fn, index, fn.emit(incr, source), source)\n\n\tbody := fn.newBasicBlock(\"rangeindex.body\")\n\tdone = fn.newBasicBlock(\"rangeindex.done\")\n\temitIf(fn, emitCompare(fn, token.LSS, incr, length.load(), source), body, done, source)\n\tfn.currentBlock = body\n\n\tk = emitLoad(fn, index, source)\n\tif tv != nil {\n\t\tx := xAlloc.load()\n\t\tswitch t := typeutil.CoreType(x.Type()).Underlying().(type) {\n\t\tcase *types.Array:\n\t\t\tinstr := &Index{\n\t\t\t\tX:     x,\n\t\t\t\tIndex: k,\n\t\t\t}\n\t\t\tinstr.setType(t.Elem())\n\t\t\tv = fn.emit(instr, source)\n\n\t\tcase *types.Pointer: // *array\n\t\t\tinstr := &IndexAddr{\n\t\t\t\tX:     x,\n\t\t\t\tIndex: k,\n\t\t\t}\n\t\t\tinstr.setType(types.NewPointer(t.Elem().Underlying().(*types.Array).Elem()))\n\t\t\tv = emitLoad(fn, fn.emit(instr, source), source)\n\n\t\tcase *types.Slice:\n\t\t\tinstr := &IndexAddr{\n\t\t\t\tX:     x,\n\t\t\t\tIndex: k,\n\t\t\t}\n\t\t\tinstr.setType(types.NewPointer(t.Elem()))\n\t\t\tv = emitLoad(fn, fn.emit(instr, source), source)\n\n\t\tdefault:\n\t\t\tpanic(\"rangeIndexed x:\" + t.String())\n\t\t}\n\t}\n\treturn\n}\n\n// rangeIter emits to fn the header for a loop using\n// Range/Next/Extract to iterate over map or string value x.\n// tk and tv are the types of the key/value results k and v, or nil\n// if the respective component is not wanted.\nfunc (b *builder) rangeIter(fn *Function, x Value, tk, tv types.Type, source ast.Node) (k, v Value, loop, done *BasicBlock) {\n\t//\n\t//\tit = range x\n\t// loop:                                   (target of continue)\n\t//\tokv = next it                      (ok, key, value)\n\t//  \tok = extract okv #0\n\t// \tif ok goto body else done\n\t// body:\n\t// \tk = extract okv #1\n\t// \tv = extract okv #2\n\t//      ...body...\n\t// \tjump loop\n\t// done:                                   (target of break)\n\t//\n\n\tif tk == nil {\n\t\ttk = tInvalid\n\t}\n\tif tv == nil {\n\t\ttv = tInvalid\n\t}\n\n\trng := &Range{X: x}\n\trng.setType(typeutil.NewIterator(types.NewTuple(\n\t\tvarOk,\n\t\tnewVar(\"k\", tk),\n\t\tnewVar(\"v\", tv),\n\t)))\n\tit := newVariable(fn, rng.typ, source)\n\tit.store(fn.emit(rng, source))\n\n\tloop = fn.newBasicBlock(\"rangeiter.loop\")\n\temitJump(fn, loop, source)\n\tfn.currentBlock = loop\n\n\t// Go doesn't currently allow ranging over string|[]byte, so isString is decidable.\n\t_, isString := typeutil.CoreType(x.Type()).Underlying().(*types.Basic)\n\n\tokvInstr := &Next{\n\t\tIter:     it.load(),\n\t\tIsString: isString,\n\t}\n\tokvInstr.setType(rng.typ.(*typeutil.Iterator).Elem())\n\tfn.emit(okvInstr, source)\n\tokv := newVariable(fn, okvInstr.Type(), source)\n\tokv.store(okvInstr)\n\n\tbody := fn.newBasicBlock(\"rangeiter.body\")\n\tdone = fn.newBasicBlock(\"rangeiter.done\")\n\temitIf(fn, emitExtract(fn, okv.load(), 0, source), body, done, source)\n\tfn.currentBlock = body\n\n\tif tk != tInvalid {\n\t\tk = emitExtract(fn, okv.load(), 1, source)\n\t}\n\tif tv != tInvalid {\n\t\tv = emitExtract(fn, okv.load(), 2, source)\n\t}\n\treturn\n}\n\n// rangeChan emits to fn the header for a loop that receives from\n// channel x until it fails.\n// tk is the channel's element type, or nil if the k result is\n// not wanted\n// pos is the position of the '=' or ':=' token.\nfunc (b *builder) rangeChan(fn *Function, x Value, tk types.Type, source ast.Node) (k Value, loop, done *BasicBlock) {\n\t//\n\t// loop:                                   (target of continue)\n\t//      ko = <-x                           (key, ok)\n\t//      ok = extract ko #1\n\t//      if ok goto body else done\n\t// body:\n\t//      k = extract ko #0\n\t//      ...\n\t//      goto loop\n\t// done:                                   (target of break)\n\n\tloop = fn.newBasicBlock(\"rangechan.loop\")\n\temitJump(fn, loop, source)\n\tfn.currentBlock = loop\n\n\trecv := emitRecv(fn, x, true, types.NewTuple(newVar(\"k\", typeutil.CoreType(x.Type()).Underlying().(*types.Chan).Elem()), varOk), source)\n\tretv := newVariable(fn, recv.Type(), source)\n\tretv.store(recv)\n\n\tbody := fn.newBasicBlock(\"rangechan.body\")\n\tdone = fn.newBasicBlock(\"rangechan.done\")\n\temitIf(fn, emitExtract(fn, retv.load(), 1, source), body, done, source)\n\tfn.currentBlock = body\n\tif tk != nil {\n\t\tk = emitExtract(fn, retv.load(), 0, source)\n\t}\n\treturn\n}\n\n// rangeInt emits to fn the header for a range loop with an integer operand.\n// tk is the key value's type, or nil if the k result is not wanted.\n// pos is the position of the \"for\" token.\nfunc (b *builder) rangeInt(fn *Function, x Value, tk types.Type, source ast.Node) (k Value, loop, done *BasicBlock) {\n\t//\n\t//     iter = 0\n\t//     if 0 < x goto body else done\n\t// loop:                                   (target of continue)\n\t//     iter++\n\t//     if iter < x goto body else done\n\t// body:\n\t//     k = x\n\t//     ...body...\n\t//     jump loop\n\t// done:                                   (target of break)\n\n\tif b, ok := x.Type().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {\n\t\tx = emitConv(fn, x, tInt, source)\n\t}\n\n\tT := x.Type()\n\titer := emitLocal(fn, T, source, \"rangeint.iter\")\n\t// x may be unsigned. Avoid initializing x to -1.\n\n\tbody := fn.newBasicBlock(\"rangeint.body\")\n\tdone = fn.newBasicBlock(\"rangeint.done\")\n\temitIf(fn, emitCompare(fn, token.LSS, emitConst(fn, zeroConst(T, source)), x, source), body, done, source)\n\n\tloop = fn.newBasicBlock(\"rangeint.loop\")\n\tfn.currentBlock = loop\n\n\tincr := &BinOp{\n\t\tOp: token.ADD,\n\t\tX:  emitLoad(fn, iter, source),\n\t\tY:  emitConv(fn, emitConst(fn, intConst(1, source)), T, source),\n\t}\n\tincr.setType(T)\n\temitStore(fn, iter, fn.emit(incr, source), source)\n\temitIf(fn, emitCompare(fn, token.LSS, incr, x, source), body, done, source)\n\tfn.currentBlock = body\n\n\tif tk != nil {\n\t\t// Integer types (int, uint8, etc.) are named and\n\t\t// we know that k is assignable to x when tk != nil.\n\t\t// This implies tk and T are identical so no conversion is needed.\n\t\tk = emitLoad(fn, iter, source)\n\t}\n\n\treturn\n}\n\ntype variable struct {\n\talloc  *Alloc\n\tfn     *Function\n\tsource ast.Node\n}\n\nfunc newVariable(fn *Function, typ types.Type, source ast.Node) *variable {\n\talloc := &Alloc{}\n\talloc.setType(types.NewPointer(typ))\n\tfn.emit(alloc, source)\n\tfn.Locals = append(fn.Locals, alloc)\n\treturn &variable{\n\t\talloc:  alloc,\n\t\tfn:     fn,\n\t\tsource: source,\n\t}\n}\n\nfunc (v *variable) store(sv Value) {\n\temitStore(v.fn, v.alloc, sv, v.source)\n}\n\nfunc (v *variable) load() Value {\n\treturn emitLoad(v.fn, v.alloc, v.source)\n}\n\n// rangeStmt emits to fn code for the range statement s, optionally\n// labelled by label.\nfunc (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock, source ast.Node) {\n\tvar tk, tv types.Type\n\tif s.Key != nil && !isBlankIdent(s.Key) {\n\t\ttk = fn.Pkg.typeOf(s.Key)\n\t}\n\tif s.Value != nil && !isBlankIdent(s.Value) {\n\t\ttv = fn.Pkg.typeOf(s.Value)\n\t}\n\n\t// create locals for s.Key and s.Value\n\tcreateVars := func() {\n\t\t// Unlike a short variable declaration, a RangeStmt\n\t\t// using := never redeclares an existing variable; it\n\t\t// always creates a new one.\n\t\tif tk != nil {\n\t\t\tid := s.Key.(*ast.Ident)\n\t\t\temitLocalVar(fn, identVar(fn, id), id)\n\t\t}\n\t\tif tv != nil {\n\t\t\tid := s.Value.(*ast.Ident)\n\t\t\temitLocalVar(fn, identVar(fn, id), id)\n\t\t}\n\t}\n\n\tafterGo122 := version.Compare(fn.goversion, \"go1.22\") >= 0\n\n\tif s.Tok == token.DEFINE && !afterGo122 {\n\t\t// pre-go1.22: If iteration variables are defined (:=), this\n\t\t// occurs once outside the loop.\n\t\tcreateVars()\n\t}\n\n\tx := b.expr(fn, s.X)\n\n\tvar k, v Value\n\tvar loop, done *BasicBlock\n\tswitch rt := typeutil.CoreType(x.Type()).Underlying().(type) {\n\tcase *types.Slice, *types.Array, *types.Pointer: // *array\n\t\tk, v, loop, done = b.rangeIndexed(fn, x, tv, source)\n\n\tcase *types.Chan:\n\t\tk, loop, done = b.rangeChan(fn, x, tk, source)\n\n\tcase *types.Map:\n\t\tk, v, loop, done = b.rangeIter(fn, x, tk, tv, source)\n\n\tcase *types.Basic:\n\t\tswitch {\n\t\tcase rt.Info()&types.IsString != 0:\n\t\t\tk, v, loop, done = b.rangeIter(fn, x, tk, tv, source)\n\n\t\tcase rt.Info()&types.IsInteger != 0:\n\t\t\tk, loop, done = b.rangeInt(fn, x, tk, source)\n\n\t\tdefault:\n\t\t\tpanic(\"Cannot range over basic type: \" + rt.String())\n\t\t}\n\n\tcase *types.Signature:\n\t\t// Special case rewrite (fn.goversion >= go1.23):\n\t\t//      for x := range f { ... }\n\t\t// into\n\t\t//      f(func(x T) bool { ... })\n\t\tb.rangeFunc(fn, x, tk, tv, s, label)\n\t\treturn\n\n\tdefault:\n\t\tpanic(\"Cannot range over: \" + rt.String())\n\t}\n\n\tif s.Tok == token.DEFINE && afterGo122 {\n\t\t// go1.22: If iteration variables are defined (:=), this occurs inside the loop.\n\t\tcreateVars()\n\t}\n\n\t// Evaluate both LHS expressions before we update either.\n\tvar kl, vl lvalue\n\tif tk != nil {\n\t\tkl = b.addr(fn, s.Key, false) // non-escaping\n\t}\n\tif tv != nil {\n\t\tvl = b.addr(fn, s.Value, false) // non-escaping\n\t}\n\tif tk != nil {\n\t\tkl.store(fn, k, s)\n\t}\n\tif tv != nil {\n\t\tvl.store(fn, v, s)\n\t}\n\n\tif label != nil {\n\t\tlabel._break = done\n\t\tlabel._continue = loop\n\t}\n\n\tfn.targets = &targets{\n\t\ttail:      fn.targets,\n\t\t_break:    done,\n\t\t_continue: loop,\n\t}\n\tb.stmt(fn, s.Body)\n\tfn.targets = fn.targets.tail\n\temitJump(fn, loop, source) // back-edge\n\tfn.currentBlock = done\n}\n\n// rangeFunc emits to fn code for the range-over-func rng.Body of the iterator\n// function x, optionally labelled by label. It creates a new anonymous function\n// yield for rng and builds the function.\nfunc (b *builder) rangeFunc(fn *Function, x Value, tk, tv types.Type, rng *ast.RangeStmt, label *lblock) {\n\t// Consider the SSA code for the outermost range-over-func in fn:\n\t//\n\t//   func fn(...) (ret R) {\n\t//     ...\n\t//     for k, v = range x {\n\t//           ...\n\t//     }\n\t//     ...\n\t//   }\n\t//\n\t// The code emitted into fn will look something like this.\n\t//\n\t// loop:\n\t//     jump := READY\n\t//     y := make closure yield [ret, deferstack, jump, k, v]\n\t//     x(y)\n\t//     switch jump {\n\t//        [see resuming execution]\n\t//     }\n\t//     goto done\n\t// done:\n\t//     ...\n\t//\n\t// where yield is a new synthetic yield function:\n\t//\n\t// func yield(_k tk, _v tv) bool\n\t//   free variables: [ret, stack, jump, k, v]\n\t// {\n\t//    entry:\n\t//      if jump != READY then goto invalid else valid\n\t//    invalid:\n\t//      panic(\"iterator called when it is not in a ready state\")\n\t//    valid:\n\t//      jump = BUSY\n\t//      k = _k\n\t//      v = _v\n\t//    ...\n\t//    cont:\n\t//      jump = READY\n\t//      return true\n\t// }\n\t//\n\t// Yield state:\n\t//\n\t// Each range loop has an associated jump variable that records\n\t// the state of the iterator. A yield function is initially\n\t// in a READY (0) and callable state.  If the yield function is called\n\t// and is not in READY state, it panics. When it is called in a callable\n\t// state, it becomes BUSY. When execution reaches the end of the body\n\t// of the loop (or a continue statement targeting the loop is executed),\n\t// the yield function returns true and resumes being in a READY state.\n\t// After the iterator function x(y) returns, then if the yield function\n\t// is in a READY state, the yield enters the DONE state.\n\t//\n\t// Each lowered control statement (break X, continue X, goto Z, or return)\n\t// that exits the loop sets the variable to a unique positive EXIT value,\n\t// before returning false from the yield function.\n\t//\n\t// If the yield function returns abruptly due to a panic or GoExit,\n\t// it remains in a BUSY state. The generated code asserts that, after\n\t// the iterator call x(y) returns normally, the jump variable state\n\t// is DONE.\n\t//\n\t// Resuming execution:\n\t//\n\t// The code generated for the range statement checks the jump\n\t// variable to determine how to resume execution.\n\t//\n\t//    switch jump {\n\t//    case BUSY:  panic(\"...\")\n\t//    case DONE:  goto done\n\t//    case READY: state = DONE; goto done\n\t//    case 123:   ... // action for exit 123.\n\t//    case 456:   ... // action for exit 456.\n\t//    ...\n\t//    }\n\t//\n\t// Forward goto statements within a yield are jumps to labels that\n\t// have not yet been traversed in fn. They may be in the Body of the\n\t// function. What we emit for these is:\n\t//\n\t//    goto target\n\t//  target:\n\t//    ...\n\t//\n\t// We leave an unresolved exit in yield.exits to check at the end\n\t// of building yield if it encountered target in the body. If it\n\t// encountered target, no additional work is required. Otherwise,\n\t// the yield emits a new early exit in the basic block for target.\n\t// We expect that blockopt will fuse the early exit into the case\n\t// block later. The unresolved exit is then added to yield.parent.exits.\n\n\tloop := fn.newBasicBlock(\"rangefunc.loop\")\n\tdone := fn.newBasicBlock(\"rangefunc.done\")\n\n\t// These are targets within y.\n\tfn.targets = &targets{\n\t\ttail:   fn.targets,\n\t\t_break: done,\n\t\t// _continue is within y.\n\t}\n\tif label != nil {\n\t\tlabel._break = done\n\t\t// _continue is within y\n\t}\n\n\temitJump(fn, loop, nil)\n\tfn.currentBlock = loop\n\n\t// loop:\n\t//     jump := READY\n\n\tanonIdx := len(fn.AnonFuncs)\n\n\tjump := newVar(fmt.Sprintf(\"jump$%d\", anonIdx+1), tInt)\n\temitLocalVar(fn, jump, nil) // zero value is READY\n\n\txsig := typeutil.CoreType(x.Type()).(*types.Signature)\n\tysig := typeutil.CoreType(xsig.Params().At(0).Type()).(*types.Signature)\n\n\t/* synthetic yield function for body of range-over-func loop */\n\ty := &Function{\n\t\tname:         fmt.Sprintf(\"%s$%d\", fn.Name(), anonIdx+1),\n\t\tSignature:    ysig,\n\t\tSynthetic:    SyntheticRangeOverFuncYield,\n\t\tparent:       fn,\n\t\tPkg:          fn.Pkg,\n\t\tProg:         fn.Prog,\n\t\tfunctionBody: new(functionBody),\n\t}\n\ty.source = rng\n\ty.goversion = fn.goversion\n\ty.jump = jump\n\ty.deferstack = fn.deferstack\n\ty.returnVars = fn.returnVars // use the parent's return variables\n\ty.uniq = fn.uniq             // start from parent's unique values\n\n\t// If the RangeStmt has a label, this is how it is passed to buildYieldFunc.\n\tif label != nil {\n\t\ty.lblocks = map[*types.Label]*lblock{label.label: nil}\n\t}\n\tfn.AnonFuncs = append(fn.AnonFuncs, y)\n\n\t// Build y immediately. It may:\n\t// * cause fn's locals to escape, and\n\t// * create new exit nodes in exits.\n\t// (y is not marked 'built' until the end of the enclosing FuncDecl.)\n\tunresolved := len(fn.exits)\n\tb.buildYieldFunc(y)\n\tfn.uniq = y.uniq // resume after y's unique values\n\n\t// Emit the call of y.\n\t//   c := MakeClosure y\n\t//   x(c)\n\tc := &MakeClosure{Fn: y}\n\tc.setType(ysig)\n\tc.comment = \"yield\"\n\tfor _, fv := range y.FreeVars {\n\t\tc.Bindings = append(c.Bindings, fv.outer)\n\t\tfv.outer = nil\n\t}\n\tfn.emit(c, nil)\n\tcall := Call{\n\t\tCall: CallCommon{\n\t\t\tValue: x,\n\t\t\tArgs:  []Value{c},\n\t\t},\n\t}\n\tcall.setType(xsig.Results())\n\tfn.emit(&call, nil)\n\n\texits := fn.exits[unresolved:]\n\tb.buildYieldResume(fn, jump, exits, done)\n\n\tfn.currentBlock = done\n\t// pop the stack for the range-over-func\n\tfn.targets = fn.targets.tail\n}\n\n// buildYieldResume emits to fn code for how to resume execution once a call to\n// the iterator function over the yield function returns x(y). It does this by building\n// a switch over the value of jump for when it is READY, BUSY, or EXIT(id).\nfunc (b *builder) buildYieldResume(fn *Function, jump *types.Var, exits []*exit, done *BasicBlock) {\n\t//    v := *jump\n\t//    switch v {\n\t//    case BUSY:    panic(\"...\")\n\t//    case READY:   jump = DONE; goto done\n\t//    case EXIT(a): ...\n\t//    case EXIT(b): ...\n\t//    ...\n\t//    }\n\tv := emitLoad(fn, fn.lookup(jump, false), nil)\n\n\tentry := fn.currentBlock\n\tbodies := make([]*BasicBlock, 2, 2+len(exits))\n\tbodies[0] = fn.newBasicBlock(\"rangefunc.resume.busy\")\n\tbodies[1] = fn.newBasicBlock(\"rangefunc.resume.ready\")\n\n\tconds := make([]Value, 2, 2+len(exits))\n\tconds[0] = emitConst(fn, jBusy())\n\tconds[1] = emitConst(fn, jReady())\n\n\tfn.currentBlock = bodies[0]\n\tfn.emit(\n\t\t&Panic{\n\t\t\tX: emitConv(fn, emitConst(fn, stringConst(\"iterator call did not preserve panic\", nil)), tEface, nil),\n\t\t},\n\t\tnil,\n\t)\n\taddEdge(fn.currentBlock, fn.Exit)\n\n\tfn.currentBlock = bodies[1]\n\tstoreVar(fn, jump, emitConst(fn, jDone()), nil)\n\temitJump(fn, done, nil)\n\n\tfor _, e := range exits {\n\t\tbody := fn.newBasicBlock(fmt.Sprintf(\"rangefunc.resume.exit.%d\", e.id))\n\t\tbodies = append(bodies, body)\n\t\tid_ := intConst(e.id, nil)\n\t\tid_.comment = fmt.Sprintf(\"rangefunc.exit.%d\", e.id)\n\t\tid := emitConst(fn, id_)\n\t\tconds = append(conds, id)\n\n\t\tfn.currentBlock = body\n\t\tswitch {\n\t\tcase e.label != nil: // forward goto?\n\t\t\t// case EXIT(id): goto lb // label\n\t\t\tlb := fn.lblockOf(e.label)\n\t\t\t// Do not mark lb as resolved.\n\t\t\t// If fn does not contain label, lb remains unresolved and\n\t\t\t// fn must itself be a range-over-func function. lb will be:\n\t\t\t//   lb:\n\t\t\t//     fn.jump = id\n\t\t\t//     return false\n\t\t\temitJump(fn, lb._goto, e.source)\n\n\t\tcase e.to != fn: // e jumps to an ancestor of fn?\n\t\t\t// case EXIT(id): { fn.jump = id; return false }\n\t\t\t// fn is a range-over-func function.\n\n\t\t\tstoreVar(fn, fn.jump, id, e.source)\n\t\t\tvFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, e.source))\n\t\t\temitReturn(fn, []Value{vFalse}, e.source)\n\n\t\tcase e.block == nil && e.label == nil: // return from fn?\n\t\t\t// case EXIT(id): { return ... }\n\n\t\t\t// The results have already been stored to variables in fn.results, so\n\t\t\t// emitReturn doesn't have to do it again.\n\t\t\temitReturn(fn, nil, e.source)\n\n\t\tcase e.block != nil:\n\t\t\t// case EXIT(id): goto block\n\t\t\temitJump(fn, e.block, e.source)\n\n\t\tdefault:\n\t\t\tpanic(\"unreachable\")\n\t\t}\n\n\t}\n\n\tfn.currentBlock = entry\n\t// Note that this switch does not have an implicit default case. This wouldn't be\n\t// valid for a user-provided switch statement, but for range-over-func we know all\n\t// possible values and we can avoid the impossible branch.\n\tswtch := &ConstantSwitch{\n\t\tTag:   v,\n\t\tConds: conds,\n\t}\n\tfn.emit(swtch, nil)\n\tfor _, body := range bodies {\n\t\taddEdge(entry, body)\n\t}\n}\n\n// stmt lowers statement s to IR form, emitting code to fn.\nfunc (b *builder) stmt(fn *Function, _s ast.Stmt) {\n\t// The label of the current statement.  If non-nil, its _goto\n\t// target is always set; its _break and _continue are set only\n\t// within the body of switch/typeswitch/select/for/range.\n\t// It is effectively an additional default-nil parameter of stmt().\n\tvar label *lblock\nstart:\n\tswitch s := _s.(type) {\n\tcase *ast.EmptyStmt:\n\t\t// ignore.  (Usually removed by gofmt.)\n\n\tcase *ast.DeclStmt: // Con, Var or Typ\n\t\td := s.Decl.(*ast.GenDecl)\n\t\tif d.Tok == token.VAR {\n\t\t\tfor _, spec := range d.Specs {\n\t\t\t\tif vs, ok := spec.(*ast.ValueSpec); ok {\n\t\t\t\t\tb.localValueSpec(fn, vs)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tcase *ast.LabeledStmt:\n\t\tif s.Label.Name == \"_\" {\n\t\t\t// Blank labels can't be the target of a goto, break,\n\t\t\t// or continue statement, so we don't need a new block.\n\t\t\t_s = s.Stmt\n\t\t\tgoto start\n\t\t}\n\t\tlabel = fn.lblockOf(fn.label(s.Label))\n\t\tlabel.resolved = true\n\t\temitJump(fn, label._goto, s)\n\t\tfn.currentBlock = label._goto\n\t\t_s = s.Stmt\n\t\tgoto start // effectively: tailcall stmt(fn, s.Stmt, label)\n\n\tcase *ast.ExprStmt:\n\t\tb.expr(fn, s.X)\n\n\tcase *ast.SendStmt:\n\t\tinstr := &Send{\n\t\t\tChan: b.expr(fn, s.Chan),\n\t\t\tX: emitConv(fn, b.expr(fn, s.Value),\n\t\t\t\ttypeutil.CoreType(fn.Pkg.typeOf(s.Chan)).Underlying().(*types.Chan).Elem(), s),\n\t\t}\n\t\tfn.emit(instr, s)\n\n\tcase *ast.IncDecStmt:\n\t\top := token.ADD\n\t\tif s.Tok == token.DEC {\n\t\t\top = token.SUB\n\t\t}\n\t\tloc := b.addr(fn, s.X, false)\n\t\tb.assignOp(fn, loc, emitConst(fn, NewConst(constant.MakeInt64(1), loc.typ(), s)), op, s)\n\n\tcase *ast.AssignStmt:\n\t\tswitch s.Tok {\n\t\tcase token.ASSIGN, token.DEFINE:\n\t\t\tb.assignStmt(fn, s.Lhs, s.Rhs, s.Tok == token.DEFINE, _s)\n\n\t\tdefault: // +=, etc.\n\t\t\top := s.Tok + token.ADD - token.ADD_ASSIGN\n\t\t\tb.assignOp(fn, b.addr(fn, s.Lhs[0], false), b.expr(fn, s.Rhs[0]), op, s)\n\t\t}\n\n\tcase *ast.GoStmt:\n\t\t// The \"intrinsics\" new/make/len/cap are forbidden here.\n\t\t// panic is treated like an ordinary function call.\n\t\tv := Go{}\n\t\tb.setCall(fn, s.Call, &v.Call)\n\t\tfn.emit(&v, s)\n\n\tcase *ast.DeferStmt:\n\t\t// The \"intrinsics\" new/make/len/cap are forbidden here.\n\t\t// panic is treated like an ordinary function call.\n\t\tdeferstack := emitLoad(fn, fn.lookup(fn.deferstack, false), s)\n\t\tv := Defer{_DeferStack: deferstack}\n\t\tb.setCall(fn, s.Call, &v.Call)\n\t\tfn.hasDefer = true\n\t\tfn.emit(&v, s)\n\n\tcase *ast.ReturnStmt:\n\t\tb.returnStmt(fn, s)\n\n\tcase *ast.BranchStmt:\n\t\tb.branchStmt(fn, s)\n\n\tcase *ast.BlockStmt:\n\t\tb.stmtList(fn, s.List)\n\n\tcase *ast.IfStmt:\n\t\tif s.Init != nil {\n\t\t\tb.stmt(fn, s.Init)\n\t\t}\n\t\tthen := fn.newBasicBlock(\"if.then\")\n\t\tdone := fn.newBasicBlock(\"if.done\")\n\t\tels := done\n\t\tif s.Else != nil {\n\t\t\tels = fn.newBasicBlock(\"if.else\")\n\t\t}\n\t\tinstr := b.cond(fn, s.Cond, then, els)\n\t\tinstr.source = s\n\t\tfn.currentBlock = then\n\t\tb.stmt(fn, s.Body)\n\t\temitJump(fn, done, s)\n\n\t\tif s.Else != nil {\n\t\t\tfn.currentBlock = els\n\t\t\tb.stmt(fn, s.Else)\n\t\t\temitJump(fn, done, s)\n\t\t}\n\n\t\tfn.currentBlock = done\n\n\tcase *ast.SwitchStmt:\n\t\tb.switchStmt(fn, s, label)\n\n\tcase *ast.TypeSwitchStmt:\n\t\tb.typeSwitchStmt(fn, s, label)\n\n\tcase *ast.SelectStmt:\n\t\tif b.selectStmt(fn, s, label) {\n\t\t\t// the select has no cases, it blocks forever\n\t\t\tfn.currentBlock = fn.newBasicBlock(\"unreachable\")\n\t\t}\n\n\tcase *ast.ForStmt:\n\t\tb.forStmt(fn, s, label)\n\n\tcase *ast.RangeStmt:\n\t\tb.rangeStmt(fn, s, label, s)\n\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unexpected statement kind: %T\", s))\n\t}\n}\n\nfunc (b *builder) branchStmt(fn *Function, s *ast.BranchStmt) {\n\tvar block *BasicBlock\n\tif s.Label == nil {\n\t\tblock = targetedBlock(fn, s.Tok)\n\t} else {\n\t\ttarget := fn.label(s.Label)\n\t\tblock = labelledBlock(fn, target, s.Tok)\n\t\tif block == nil { // forward goto\n\t\t\tlb := fn.lblockOf(target)\n\t\t\tblock = lb._goto // jump to lb._goto\n\t\t\tif fn.jump != nil {\n\t\t\t\t// fn is a range-over-func and the goto may exit fn.\n\t\t\t\t// Create an exit and resolve it at the end of\n\t\t\t\t// builder.buildYieldFunc.\n\t\t\t\tlabelExit(fn, target, s)\n\t\t\t}\n\t\t}\n\t}\n\tto := block.parent\n\n\tif to == fn {\n\t\temitJump(fn, block, s)\n\t} else { // break outside of fn.\n\t\t// fn must be a range-over-func\n\t\te := blockExit(fn, block, s)\n\t\tid_ := intConst(e.id, s)\n\t\tid_.comment = fmt.Sprintf(\"rangefunc.exit.%d\", e.id)\n\t\tid := emitConst(fn, id_)\n\t\tstoreVar(fn, fn.jump, id, s)\n\t\tvFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, s))\n\t\temitReturn(fn, []Value{vFalse}, s)\n\t}\n\tfn.currentBlock = fn.newBasicBlock(\"unreachable\")\n}\n\nfunc (b *builder) returnStmt(fn *Function, s *ast.ReturnStmt) {\n\t// TODO(dh): we could emit tighter position information by\n\t// using the ith returned expression\n\n\tvar results []Value\n\n\tsig := fn.sourceFn.Signature // signature of the enclosing source function\n\n\t// Convert return operands to result type.\n\tif len(s.Results) == 1 && sig.Results().Len() > 1 {\n\t\t// Return of one expression in a multi-valued function.\n\t\ttuple := b.exprN(fn, s.Results[0])\n\t\tttuple := tuple.Type().(*types.Tuple)\n\t\tfor i, n := 0, ttuple.Len(); i < n; i++ {\n\t\t\tresults = append(results,\n\t\t\t\temitConv(fn, emitExtract(fn, tuple, i, s),\n\t\t\t\t\tsig.Results().At(i).Type(), s))\n\t\t}\n\t} else {\n\t\t// 1:1 return, or no-arg return in non-void function.\n\t\tfor i, r := range s.Results {\n\t\t\tv := emitConv(fn, b.expr(fn, r), sig.Results().At(i).Type(), s)\n\t\t\tresults = append(results, v)\n\t\t}\n\t}\n\n\t// Store the results.\n\tfor i, r := range results {\n\t\tvar result Value // fn.sourceFn.result[i] conceptually\n\t\tif fn == fn.sourceFn {\n\t\t\tresult = fn.results[i]\n\t\t} else { // lookup needed?\n\t\t\tresult = fn.lookup(fn.returnVars[i], false)\n\t\t}\n\t\temitStore(fn, result, r, s)\n\t}\n\n\tif fn.jump != nil {\n\t\t// Return from body of a range-over-func.\n\t\t// The return statement is syntactically within the loop,\n\t\t// but the generated code is in the 'switch jump {...}' after it.\n\t\te := returnExit(fn, s)\n\t\tid_ := intConst(e.id, e.source)\n\t\tid_.comment = fmt.Sprintf(\"rangefunc.exit.%d\", e.id)\n\t\tid := emitConst(fn, id_)\n\t\tstoreVar(fn, fn.jump, id, e.source)\n\t\tvFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, e.source))\n\t\temitReturn(fn, []Value{vFalse}, e.source)\n\t\treturn\n\t}\n\n\t// The results have already been stored to variables in fn.results, so\n\t// emitReturn doesn't have to do it again.\n\temitReturn(fn, nil, s)\n}\n\nfunc emitReturn(fn *Function, results []Value, source ast.Node) {\n\tfor i, r := range results {\n\t\temitStore(fn, fn.results[i], r, source)\n\t}\n\n\temitJump(fn, fn.Exit, source)\n\tfn.currentBlock = fn.newBasicBlock(\"unreachable\")\n}\n\n// buildFunction builds IR code for the body of function fn.  Idempotent.\nfunc (b *builder) buildFunction(fn *Function) {\n\tif fn.Blocks != nil {\n\t\treturn // building already started\n\t}\n\n\tvar recvField *ast.FieldList\n\tvar body *ast.BlockStmt\n\tvar functype *ast.FuncType\n\tswitch n := fn.source.(type) {\n\tcase nil:\n\t\treturn // not a Go source function.  (Synthetic, or from object file.)\n\tcase *ast.FuncDecl:\n\t\tfunctype = n.Type\n\t\trecvField = n.Recv\n\t\tbody = n.Body\n\tcase *ast.FuncLit:\n\t\tfunctype = n.Type\n\t\tbody = n.Body\n\tdefault:\n\t\tpanic(n)\n\t}\n\n\tif body == nil {\n\t\t// External function.\n\t\tif fn.Params == nil {\n\t\t\t// This condition ensures we add a non-empty\n\t\t\t// params list once only, but we may attempt\n\t\t\t// the degenerate empty case repeatedly.\n\t\t\t// TODO(adonovan): opt: don't do that.\n\n\t\t\t// We set Function.Params even though there is no body\n\t\t\t// code to reference them.  This simplifies clients.\n\t\t\tif recv := fn.Signature.Recv(); recv != nil {\n\t\t\t\t// XXX synthesize an ast.Node\n\t\t\t\tfn.addParamVar(recv, nil)\n\t\t\t}\n\t\t\tparams := fn.Signature.Params()\n\t\t\tfor i, n := 0, params.Len(); i < n; i++ {\n\t\t\t\t// XXX synthesize an ast.Node\n\t\t\t\tfn.addParamVar(params.At(i), nil)\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\tif fn.Prog.mode&LogSource != 0 {\n\t\tdefer logStack(\"build function %s @ %s\", fn, fn.Prog.Fset.Position(fn.Pos()))()\n\t}\n\tfn.blocksets = b.blocksets\n\tfn.Blocks = make([]*BasicBlock, 0, avgBlocks)\n\tfn.sourceFn = fn\n\tfn.startBody()\n\tfn.createSyntacticParams(recvField, functype)\n\tfn.createDeferStack()\n\tfn.exitBlock()\n\tb.stmt(fn, body)\n\tif cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) {\n\t\t// Control fell off the end of the function's body block.\n\t\t//\n\t\t// Block optimizations eliminate the current block, if\n\t\t// unreachable.  It is a builder invariant that\n\t\t// if this no-arg return is ill-typed for\n\t\t// fn.Signature.Results, this block must be\n\t\t// unreachable.  The sanity checker checks this.\n\t\t// fn.emit(new(RunDefers))\n\t\t// fn.emit(new(Return))\n\t\temitJump(fn, fn.Exit, nil)\n\t}\n\toptimizeBlocks(fn)\n\tbuildFakeExits(fn)\n\tfn.finishBody()\n\tb.blocksets = fn.blocksets\n\tfn.functionBody = nil\n}\n\n// buildYieldFunc builds the body of the yield function created\n// from a range-over-func *ast.RangeStmt.\nfunc (b *builder) buildYieldFunc(fn *Function) {\n\t// See builder.rangeFunc for detailed documentation on how fn is set up.\n\t//\n\t// In pseudo-Go this roughly builds:\n\t// func yield(_k tk, _v tv) bool {\n\t//         if jump != READY { panic(\"yield function called after range loop exit\") }\n\t//     jump = BUSY\n\t//     k, v = _k, _v // assign the iterator variable (if needed)\n\t//     ... // rng.Body\n\t//   continue:\n\t//     jump = READY\n\t//     return true\n\t// }\n\ts := fn.source.(*ast.RangeStmt)\n\tfn.sourceFn = fn.parent.sourceFn\n\tfn.startBody()\n\tparams := fn.Signature.Params()\n\tfor v := range params.Variables() {\n\t\tfn.addParamVar(v, nil)\n\t}\n\tfn.addResultVar(fn.Signature.Results().At(0), nil)\n\tfn.exitBlock()\n\n\t// Initial targets\n\tycont := fn.newBasicBlock(\"yield-continue\")\n\t// lblocks is either {} or is {label: nil} where label is the label of syntax.\n\tfor label := range fn.lblocks {\n\t\tfn.lblocks[label] = &lblock{\n\t\t\tlabel:     label,\n\t\t\tresolved:  true,\n\t\t\t_goto:     ycont,\n\t\t\t_continue: ycont,\n\t\t\t// `break label` statement targets fn.parent.targets._break\n\t\t}\n\t}\n\tfn.targets = &targets{\n\t\ttail:      fn.targets,\n\t\t_continue: ycont,\n\t\t// `break` statement targets fn.parent.targets._break.\n\t}\n\n\t// continue:\n\t//   jump = READY\n\t//   return true\n\tsaved := fn.currentBlock\n\tfn.currentBlock = ycont\n\tstoreVar(fn, fn.jump, emitConst(fn, jReady()), s.Body)\n\tvTrue := emitConst(fn, NewConst(constant.MakeBool(true), tBool, nil))\n\temitReturn(fn, []Value{vTrue}, nil)\n\n\t// Emit header:\n\t//\n\t//   if jump != READY { panic(\"yield iterator accessed after exit\") }\n\t//   jump = BUSY\n\t//   k, v = _k, _v\n\tfn.currentBlock = saved\n\tyloop := fn.newBasicBlock(\"yield-loop\")\n\tinvalid := fn.newBasicBlock(\"yield-invalid\")\n\n\tjumpVal := emitLoad(fn, fn.lookup(fn.jump, true), nil)\n\temitIf(fn, emitCompare(fn, token.EQL, jumpVal, emitConst(fn, jReady()), nil), yloop, invalid, nil)\n\tfn.currentBlock = invalid\n\tfn.emit(\n\t\t&Panic{\n\t\t\tX: emitConv(fn, emitConst(fn, stringConst(\"yield function called after range loop exit\", nil)), tEface, nil),\n\t\t},\n\t\tnil,\n\t)\n\taddEdge(fn.currentBlock, fn.Exit)\n\n\tfn.currentBlock = yloop\n\tstoreVar(fn, fn.jump, emitConst(fn, jBusy()), s.Body)\n\n\t// Initialize k and v from params.\n\tvar tk, tv types.Type\n\tif s.Key != nil && !isBlankIdent(s.Key) {\n\t\ttk = fn.Pkg.typeOf(s.Key) // fn.parent.typeOf is identical\n\t}\n\tif s.Value != nil && !isBlankIdent(s.Value) {\n\t\ttv = fn.Pkg.typeOf(s.Value)\n\t}\n\tif s.Tok == token.DEFINE {\n\t\tif tk != nil {\n\t\t\temitLocalVar(fn, identVar(fn, s.Key.(*ast.Ident)), s.Key)\n\t\t}\n\t\tif tv != nil {\n\t\t\temitLocalVar(fn, identVar(fn, s.Value.(*ast.Ident)), s.Value)\n\t\t}\n\t}\n\tvar k, v Value\n\tif len(fn.Params) > 0 {\n\t\tk = fn.Params[0]\n\t}\n\tif len(fn.Params) > 1 {\n\t\tv = fn.Params[1]\n\t}\n\tvar kl, vl lvalue\n\tif tk != nil {\n\t\tkl = b.addr(fn, s.Key, false) // non-escaping\n\t}\n\tif tv != nil {\n\t\tvl = b.addr(fn, s.Value, false) // non-escaping\n\t}\n\tif tk != nil {\n\t\tkl.store(fn, k, s.Key)\n\t}\n\tif tv != nil {\n\t\tvl.store(fn, v, s.Value)\n\t}\n\n\t// Build the body of the range loop.\n\tb.stmt(fn, s.Body)\n\tif cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) {\n\t\t// Control fell off the end of the function's body block.\n\t\t// Block optimizations eliminate the current block, if\n\t\t// unreachable.\n\t\temitJump(fn, ycont, nil)\n\t}\n\tfn.targets = fn.targets.tail\n\n\t// Clean up exits and promote any unresolved exits to fn.parent.\n\tfor _, e := range fn.exits {\n\t\tif e.label != nil {\n\t\t\tlb := fn.lblocks[e.label]\n\t\t\tif lb.resolved {\n\t\t\t\t// label was resolved. Do not turn lb into an exit.\n\t\t\t\t// e does not need to be handled by the parent.\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// _goto becomes an exit.\n\t\t\t//   _goto:\n\t\t\t//     jump = id\n\t\t\t//     return false\n\t\t\tfn.currentBlock = lb._goto\n\t\t\tid_ := intConst(e.id, e.source)\n\t\t\tid_.comment = fmt.Sprintf(\"rangefunc.exit.%d\", e.id)\n\t\t\tid := emitConst(fn, id_)\n\t\t\tstoreVar(fn, fn.jump, id, e.source)\n\t\t\tvFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, e.source))\n\t\t\temitReturn(fn, []Value{vFalse}, e.source)\n\t\t}\n\n\t\tif e.to != fn { // e needs to be handled by the parent too.\n\t\t\tfn.parent.exits = append(fn.parent.exits, e)\n\t\t}\n\t}\n\n\tfn.finishBody()\n}\n\n// buildFuncDecl builds IR code for the function or method declared\n// by decl in package pkg.\nfunc (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) {\n\tid := decl.Name\n\tfn := pkg.values[pkg.info.Defs[id]].(*Function)\n\tif decl.Recv == nil && id.Name == \"init\" {\n\t\tvar v Call\n\t\tv.Call.Value = fn\n\t\tv.setType(types.NewTuple())\n\t\tpkg.init.emit(&v, decl)\n\t}\n\tfn.source = decl\n\tb.buildFunction(fn)\n}\n\n// Build calls Package.Build for each package in prog.\n//\n// Build is intended for whole-program analysis; a typical compiler\n// need only build a single package.\n//\n// Build is idempotent and thread-safe.\nfunc (prog *Program) Build() {\n\tfor _, p := range prog.packages {\n\t\tp.Build()\n\t}\n}\n\n// Build builds IR code for all functions and vars in package p.\n//\n// Precondition: CreatePackage must have been called for all of p's\n// direct imports (and hence its direct imports must have been\n// error-free).\n//\n// Build is idempotent and thread-safe.\nfunc (p *Package) Build() { p.buildOnce.Do(p.build) }\n\nfunc (p *Package) build() {\n\tif p.info == nil {\n\t\treturn // synthetic package, e.g. \"testmain\"\n\t}\n\n\t// Ensure we have runtime type info for all exported members.\n\t// TODO(adonovan): ideally belongs in memberFromObject, but\n\t// that would require package creation in topological order.\n\tfor name, mem := range p.Members {\n\t\tif ast.IsExported(name) {\n\t\t\tp.Prog.needMethodsOf(mem.Type())\n\t\t}\n\t}\n\tif p.Prog.mode&LogSource != 0 {\n\t\tdefer logStack(\"build %s\", p)()\n\t}\n\tinit := p.init\n\tinit.startBody()\n\tinit.exitBlock()\n\n\tvar done *BasicBlock\n\n\t// Make init() skip if package is already initialized.\n\tinitguard := p.Var(\"init$guard\")\n\tdoinit := init.newBasicBlock(\"init.start\")\n\tdone = init.Exit\n\temitIf(init, emitLoad(init, initguard, nil), done, doinit, nil)\n\tinit.currentBlock = doinit\n\temitStore(init, initguard, emitConst(init, NewConst(constant.MakeBool(true), tBool, nil)), nil)\n\n\t// Call the init() function of each package we import.\n\tfor _, pkg := range p.Pkg.Imports() {\n\t\tprereq := p.Prog.packages[pkg]\n\t\tif prereq == nil {\n\t\t\tpanic(fmt.Sprintf(\"Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called\", p.Pkg.Path(), pkg.Path()))\n\t\t}\n\t\tvar v Call\n\t\tv.Call.Value = prereq.init\n\t\tv.setType(types.NewTuple())\n\t\tinit.emit(&v, nil)\n\t}\n\n\tb := builder{\n\t\tprintFunc: p.printFunc,\n\t}\n\n\t// Initialize package-level vars in correct order.\n\tfor _, varinit := range p.info.InitOrder {\n\t\tif init.Prog.mode&LogSource != 0 {\n\t\t\tfmt.Fprintf(os.Stderr, \"build global initializer %v @ %s\\n\",\n\t\t\t\tvarinit.Lhs, p.Prog.Fset.Position(varinit.Rhs.Pos()))\n\t\t}\n\t\t// Initializers for global vars are evaluated in dependency\n\t\t// order, but may come from arbitrary files of the package\n\t\t// with different versions, so we transiently update\n\t\t// init.goversion for each one. (Since init is a synthetic\n\t\t// function it has no syntax of its own that needs a version.)\n\t\tinit.goversion = p.initVersion[varinit.Rhs]\n\t\tif len(varinit.Lhs) == 1 {\n\t\t\t// 1:1 initialization: var x, y = a(), b()\n\t\t\tvar lval lvalue\n\t\t\tif v := varinit.Lhs[0]; v.Name() != \"_\" {\n\t\t\t\tlval = &address{addr: p.values[v].(*Global)}\n\t\t\t} else {\n\t\t\t\tlval = blank{}\n\t\t\t}\n\t\t\t// TODO(dh): do emit position information\n\t\t\tb.assign(init, lval, varinit.Rhs, true, nil, nil)\n\t\t} else {\n\t\t\t// n:1 initialization: var x, y :=  f()\n\t\t\ttuple := b.exprN(init, varinit.Rhs)\n\t\t\tfor i, v := range varinit.Lhs {\n\t\t\t\tif v.Name() == \"_\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\temitStore(init, p.values[v].(*Global), emitExtract(init, tuple, i, nil), nil)\n\t\t\t}\n\t\t}\n\t}\n\tinit.goversion = \"\" // The rest of the init function is synthetic. No syntax => no goversion.\n\n\t// Build all package-level functions, init functions\n\t// and methods, including unreachable/blank ones.\n\t// We build them in source order, but it's not significant.\n\tfor _, file := range p.files {\n\t\tfor _, decl := range file.Decls {\n\t\t\tif decl, ok := decl.(*ast.FuncDecl); ok {\n\t\t\t\tb.buildFuncDecl(p, decl)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Finish up init().\n\temitJump(init, done, nil)\n\tinit.finishBody()\n\n\t// We no longer need ASTs or go/types deductions.\n\tp.info = nil\n\tp.initVersion = nil\n\n\tif p.Prog.mode&SanityCheckFunctions != 0 {\n\t\tsanityCheckPackage(p)\n\t}\n}\n\n// Like ObjectOf, but panics instead of returning nil.\n// Only valid during p's create and build phases.\nfunc (p *Package) objectOf(id *ast.Ident) types.Object {\n\tif o := p.info.ObjectOf(id); o != nil {\n\t\treturn o\n\t}\n\tpanic(fmt.Sprintf(\"no types.Object for ast.Ident %s @ %s\",\n\t\tid.Name, p.Prog.Fset.Position(id.Pos())))\n}\n\n// Like TypeOf, but panics instead of returning nil.\n// Only valid during p's create and build phases.\nfunc (p *Package) typeOf(e ast.Expr) types.Type {\n\tif T := p.info.TypeOf(e); T != nil {\n\t\treturn T\n\t}\n\tpanic(fmt.Sprintf(\"no type for %T @ %s\",\n\t\te, p.Prog.Fset.Position(e.Pos())))\n}\n"
  },
  {
    "path": "go/ir/builder_test.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n//lint:file-ignore SA1019 go/ssa's test suite is built around the deprecated go/loader. We'll leave fixing that to upstream.\n\npackage ir_test\n\nimport (\n\t\"bytes\"\n\t\"go/ast\"\n\t\"go/importer\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"os\"\n\t\"reflect\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\n\t\"golang.org/x/tools/go/loader\"\n)\n\nfunc isEmpty(f *ir.Function) bool { return f.Blocks == nil }\n\n// Tests that programs partially loaded from gc object files contain\n// functions with no code for the external portions, but are otherwise ok.\nfunc TestBuildPackage(t *testing.T) {\n\tinput := `\npackage main\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"testing\"\n)\n\nfunc main() {\n\tvar t testing.T\n\tt.Parallel()    // static call to external declared method\n\tt.Fail()        // static call to promoted external declared method\n\ttesting.Short() // static call to external package-level function\n\n\tvar w io.Writer = new(bytes.Buffer)\n\tw.Write(nil)    // interface invoke of external declared method\n}\n`\n\n\t// Parse the file.\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"input.go\", input, parser.SkipObjectResolution)\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\n\t// Build an IR program from the parsed file.\n\t// Load its dependencies from gc binary export data.\n\tmainPkg, _, err := irutil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,\n\t\ttypes.NewPackage(\"main\", \"\"), []*ast.File{f}, ir.SanityCheckFunctions)\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\n\t// The main package, its direct and indirect dependencies are loaded.\n\tdeps := []string{\n\t\t// directly imported dependencies:\n\t\t\"bytes\", \"io\", \"testing\",\n\t\t// indirect dependencies mentioned by\n\t\t// the direct imports' export data\n\t\t\"sync\", \"unicode\", \"time\",\n\t}\n\n\tprog := mainPkg.Prog\n\tall := prog.AllPackages()\n\tif len(all) <= len(deps) {\n\t\tt.Errorf(\"unexpected set of loaded packages: %q\", all)\n\t}\n\tfor _, path := range deps {\n\t\tpkg := prog.ImportedPackage(path)\n\t\tif pkg == nil {\n\t\t\tt.Errorf(\"package not loaded: %q\", path)\n\t\t\tcontinue\n\t\t}\n\n\t\t// External packages should have no function bodies (except for wrappers).\n\t\tisExt := pkg != mainPkg\n\n\t\t// init()\n\t\tif isExt && !isEmpty(pkg.Func(\"init\")) {\n\t\t\tt.Errorf(\"external package %s has non-empty init\", pkg)\n\t\t} else if !isExt && isEmpty(pkg.Func(\"init\")) {\n\t\t\tt.Errorf(\"main package %s has empty init\", pkg)\n\t\t}\n\n\t\tfor _, mem := range pkg.Members {\n\t\t\tswitch mem := mem.(type) {\n\t\t\tcase *ir.Function:\n\t\t\t\t// Functions at package level.\n\t\t\t\tif isExt && !isEmpty(mem) {\n\t\t\t\t\tt.Errorf(\"external function %s is non-empty\", mem)\n\t\t\t\t} else if !isExt && isEmpty(mem) {\n\t\t\t\t\tt.Errorf(\"function %s is empty\", mem)\n\t\t\t\t}\n\n\t\t\tcase *ir.Type:\n\t\t\t\t// Methods of named types T.\n\t\t\t\t// (In this test, all exported methods belong to *T not T.)\n\t\t\t\tif !isExt {\n\t\t\t\t\tt.Fatalf(\"unexpected name type in main package: %s\", mem)\n\t\t\t\t}\n\t\t\t\tmset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))\n\t\t\t\tfor i, n := 0, mset.Len(); i < n; i++ {\n\t\t\t\t\tm := prog.MethodValue(mset.At(i))\n\t\t\t\t\t// For external types, only synthetic wrappers have code.\n\t\t\t\t\texpExt := m.Synthetic != ir.SyntheticWrapper\n\t\t\t\t\tif expExt && !isEmpty(m) {\n\t\t\t\t\t\tt.Errorf(\"external method %s is non-empty: %s\",\n\t\t\t\t\t\t\tm, m.Synthetic)\n\t\t\t\t\t} else if !expExt && isEmpty(m) {\n\t\t\t\t\t\tt.Errorf(\"method function %s is empty: %s\",\n\t\t\t\t\t\t\tm, m.Synthetic)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\texpectedCallee := []string{\n\t\t\"(*testing.T).Parallel\",\n\t\t\"(*testing.common).Fail\",\n\t\t\"testing.Short\",\n\t\t\"N/A\",\n\t}\n\tcallNum := 0\n\tfor _, b := range mainPkg.Func(\"main\").Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tswitch instr := instr.(type) {\n\t\t\tcase ir.CallInstruction:\n\t\t\t\tcall := instr.Common()\n\t\t\t\tif want := expectedCallee[callNum]; want != \"N/A\" {\n\t\t\t\t\tgot := call.StaticCallee().String()\n\t\t\t\t\tif want != got {\n\t\t\t\t\t\tt.Errorf(\"call #%d from main.main: got callee %s, want %s\",\n\t\t\t\t\t\t\tcallNum, got, want)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcallNum++\n\t\t\t}\n\t\t}\n\t}\n\tif callNum != 4 {\n\t\tt.Errorf(\"in main.main: got %d calls, want %d\", callNum, 4)\n\t}\n}\n\n// TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types.\nfunc TestRuntimeTypes(t *testing.T) {\n\ttests := []struct {\n\t\tinput string\n\t\twant  []string\n\t}{\n\t\t// An exported package-level type is needed.\n\t\t{`package A; type T struct{}; func (T) f() {}`,\n\t\t\t[]string{\"*p.T\", \"p.T\"},\n\t\t},\n\t\t// An unexported package-level type is not needed.\n\t\t{`package B; type t struct{}; func (t) f() {}`,\n\t\t\tnil,\n\t\t},\n\t\t// Subcomponents of type of exported package-level var are needed.\n\t\t{`package C; import \"bytes\"; var V struct {*bytes.Buffer}`,\n\t\t\t[]string{\"*bytes.Buffer\", \"*struct{*bytes.Buffer}\", \"struct{*bytes.Buffer}\"},\n\t\t},\n\t\t// Subcomponents of type of unexported package-level var are not needed.\n\t\t{`package D; import \"bytes\"; var v struct {*bytes.Buffer}`,\n\t\t\tnil,\n\t\t},\n\t\t// Subcomponents of type of exported package-level function are needed.\n\t\t{`package E; import \"bytes\"; func F(struct {*bytes.Buffer}) {}`,\n\t\t\t[]string{\"*bytes.Buffer\", \"struct{*bytes.Buffer}\"},\n\t\t},\n\t\t// Subcomponents of type of unexported package-level function are not needed.\n\t\t{`package F; import \"bytes\"; func f(struct {*bytes.Buffer}) {}`,\n\t\t\tnil,\n\t\t},\n\t\t// Subcomponents of type of exported method of uninstantiated unexported type are not needed.\n\t\t{`package G; import \"bytes\"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`,\n\t\t\tnil,\n\t\t},\n\t\t// ...unless used by MakeInterface.\n\t\t{`package G2; import \"bytes\"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,\n\t\t\t[]string{\"*bytes.Buffer\", \"*p.x\", \"p.x\", \"struct{*bytes.Buffer}\"},\n\t\t},\n\t\t// Subcomponents of type of unexported method are not needed.\n\t\t{`package I; import \"bytes\"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`,\n\t\t\t[]string{\"*bytes.Buffer\", \"*p.X\", \"p.X\", \"struct{*bytes.Buffer}\"},\n\t\t},\n\t\t// Local types aren't needed.\n\t\t{`package J; import \"bytes\"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,\n\t\t\tnil,\n\t\t},\n\t\t// ...unless used by MakeInterface.\n\t\t{`package K; import \"bytes\"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,\n\t\t\t[]string{\"*bytes.Buffer\", \"*p.T\", \"p.T\"},\n\t\t},\n\t\t// Types used as operand of MakeInterface are needed.\n\t\t{`package L; import \"bytes\"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,\n\t\t\t[]string{\"*bytes.Buffer\", \"struct{*bytes.Buffer}\"},\n\t\t},\n\t\t// MakeInterface is optimized away when storing to a blank.\n\t\t{`package M; import \"bytes\"; var _ interface{} = struct{*bytes.Buffer}{}`,\n\t\t\tnil,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\t// Parse the file.\n\t\tfset := token.NewFileSet()\n\t\tf, err := parser.ParseFile(fset, \"input.go\", test.input, 0)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"test %q: %s\", test.input[:15], err)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Create a single-file main package.\n\t\t// Load dependencies from gc binary export data.\n\t\tirpkg, _, err := irutil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,\n\t\t\ttypes.NewPackage(\"p\", \"\"), []*ast.File{f}, ir.SanityCheckFunctions)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"test %q: %s\", test.input[:15], err)\n\t\t\tcontinue\n\t\t}\n\n\t\tvar typstrs []string\n\t\tfor _, T := range irpkg.Prog.RuntimeTypes() {\n\t\t\ttypstrs = append(typstrs, T.String())\n\t\t}\n\t\tsort.Strings(typstrs)\n\n\t\tif !reflect.DeepEqual(typstrs, test.want) {\n\t\t\tt.Errorf(\"test 'package %s': got %q, want %q\",\n\t\t\t\tf.Name.Name, typstrs, test.want)\n\t\t}\n\t}\n}\n\n// TestInit tests that synthesized init functions are correctly formed.\nfunc TestInit(t *testing.T) {\n\ttests := []struct {\n\t\tmode        ir.BuilderMode\n\t\tinput, want string\n\t}{\n\t\t{0, `package A; import _ \"errors\"; var i int = 42`,\n\t\t\t`# Name: A.init\n# Package: A\n# Synthetic: package initializer\nfunc init():\nb0: # entry\n\tt1 = Const <bool> {true}\n\tt2 = Const <int> {42}\n\tt3 = Load <bool> init$guard\n\tIf t3 → b1 b2\n\nb1: ← b0 b2 # exit\n\tReturn\n\nb2: ← b0 # init.start\n\tStore {bool} init$guard t1\n\tt7 = Call <()> errors.init\n\tStore {int} i t2\n\tJump → b1\n\n`},\n\t}\n\tfor _, test := range tests {\n\t\t// Create a single-file main package.\n\t\tvar conf loader.Config\n\t\tf, err := conf.ParseFile(\"<input>\", test.input)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"test %q: %s\", test.input[:15], err)\n\t\t\tcontinue\n\t\t}\n\t\tconf.CreateFromFiles(f.Name.Name, f)\n\n\t\tlprog, err := conf.Load()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"test 'package %s': Load: %s\", f.Name.Name, err)\n\t\t\tcontinue\n\t\t}\n\t\tprog := irutil.CreateProgram(lprog, test.mode)\n\t\tmainPkg := prog.Package(lprog.Created[0].Pkg)\n\t\tprog.Build()\n\t\tinitFunc := mainPkg.Func(\"init\")\n\t\tif initFunc == nil {\n\t\t\tt.Errorf(\"test 'package %s': no init function\", f.Name.Name)\n\t\t\tcontinue\n\t\t}\n\n\t\tvar initbuf bytes.Buffer\n\t\t_, err = initFunc.WriteTo(&initbuf)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"test 'package %s': WriteTo: %s\", f.Name.Name, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif initbuf.String() != test.want {\n\t\t\tt.Errorf(\"test 'package %s': got %s, want %s\", f.Name.Name, initbuf.String(), test.want)\n\t\t}\n\t}\n}\n\n// TestSyntheticFuncs checks that the expected synthetic functions are\n// created, reachable, and not duplicated.\nfunc TestSyntheticFuncs(t *testing.T) {\n\tconst input = `package P\ntype T int\nfunc (T) f() int\nfunc (*T) g() int\nvar (\n\t// thunks\n\ta = T.f\n\tb = T.f\n\tc = (struct{T}).f\n\td = (struct{T}).f\n\te = (*T).g\n\tf = (*T).g\n\tg = (struct{*T}).g\n\th = (struct{*T}).g\n\n\t// bounds\n\ti = T(0).f\n\tj = T(0).f\n\tk = new(T).g\n\tl = new(T).g\n\n\t// wrappers\n\tm interface{} = struct{T}{}\n\tn interface{} = struct{T}{}\n\to interface{} = struct{*T}{}\n\tp interface{} = struct{*T}{}\n\tq interface{} = new(struct{T})\n\tr interface{} = new(struct{T})\n\ts interface{} = new(struct{*T})\n\tt interface{} = new(struct{*T})\n)\n`\n\t// Parse\n\tvar conf loader.Config\n\tf, err := conf.ParseFile(\"<input>\", input)\n\tif err != nil {\n\t\tt.Fatalf(\"parse: %v\", err)\n\t}\n\tconf.CreateFromFiles(f.Name.Name, f)\n\n\t// Load\n\tlprog, err := conf.Load()\n\tif err != nil {\n\t\tt.Fatalf(\"Load: %v\", err)\n\t}\n\n\t// Create and build IR\n\tprog := irutil.CreateProgram(lprog, 0)\n\tprog.Build()\n\n\t// Enumerate reachable synthetic functions\n\twant := map[string]ir.Synthetic{\n\t\t\"(*P.T).g$bound\": ir.SyntheticBound,\n\t\t\"(P.T).f$bound\":  ir.SyntheticBound,\n\n\t\t\"(*P.T).g$thunk\":         ir.SyntheticThunk,\n\t\t\"(P.T).f$thunk\":          ir.SyntheticThunk,\n\t\t\"(struct{*P.T}).g$thunk\": ir.SyntheticThunk,\n\t\t\"(struct{P.T}).f$thunk\":  ir.SyntheticThunk,\n\n\t\t\"(*P.T).f\":          ir.SyntheticWrapper,\n\t\t\"(*struct{*P.T}).f\": ir.SyntheticWrapper,\n\t\t\"(*struct{*P.T}).g\": ir.SyntheticWrapper,\n\t\t\"(*struct{P.T}).f\":  ir.SyntheticWrapper,\n\t\t\"(*struct{P.T}).g\":  ir.SyntheticWrapper,\n\t\t\"(struct{*P.T}).f\":  ir.SyntheticWrapper,\n\t\t\"(struct{*P.T}).g\":  ir.SyntheticWrapper,\n\t\t\"(struct{P.T}).f\":   ir.SyntheticWrapper,\n\n\t\t\"P.init\": ir.SyntheticPackageInitializer,\n\t}\n\tvar seen []string // may contain dups\n\tfor fn := range irutil.AllFunctions(prog) {\n\t\tif fn.Synthetic == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tname := fn.String()\n\t\twantDescr, ok := want[name]\n\t\tif !ok {\n\t\t\tt.Errorf(\"got unexpected/duplicate func: %q: %q\", name, fn.Synthetic)\n\t\t\tcontinue\n\t\t}\n\t\tseen = append(seen, name)\n\n\t\tif wantDescr != fn.Synthetic {\n\t\t\tt.Errorf(\"(%s).Synthetic = %q, want %q\", name, fn.Synthetic, wantDescr)\n\t\t}\n\t}\n\n\tfor _, name := range seen {\n\t\tdelete(want, name)\n\t}\n\tfor fn, descr := range want {\n\t\tt.Errorf(\"want func: %q: %q\", fn, descr)\n\t}\n}\n\n// TestPhiElimination ensures that dead phis, including those that\n// participate in a cycle, are properly eliminated.\nfunc TestPhiElimination(t *testing.T) {\n\tconst input = `\npackage p\n\nfunc f() error\n\nfunc g(slice []int) {\n\tfor {\n\t\tfor range slice {\n\t\t\t// e should not be lifted to a dead φ-node.\n\t\t\te := f()\n\t\t\th(e)\n\t\t}\n\t}\n}\n\nfunc h(error)\n`\n\t// The IR code for this function should look something like this:\n\t// 0:\n\t//         jump 1\n\t// 1:\n\t//         t0 = len(slice)\n\t//         jump 2\n\t// 2:\n\t//         t1 = phi [1: -1:int, 3: t2]\n\t//         t2 = t1 + 1:int\n\t//         t3 = t2 < t0\n\t//         if t3 goto 3 else 1\n\t// 3:\n\t//         t4 = f()\n\t//         t5 = h(t4)\n\t//         jump 2\n\t//\n\t// But earlier versions of the IR construction algorithm would\n\t// additionally generate this cycle of dead phis:\n\t//\n\t// 1:\n\t//         t7 = phi [0: nil:error, 2: t8] #e\n\t//         ...\n\t// 2:\n\t//         t8 = phi [1: t7, 3: t4] #e\n\t//         ...\n\n\t// Parse\n\tvar conf loader.Config\n\tf, err := conf.ParseFile(\"<input>\", input)\n\tif err != nil {\n\t\tt.Fatalf(\"parse: %v\", err)\n\t}\n\tconf.CreateFromFiles(\"p\", f)\n\n\t// Load\n\tlprog, err := conf.Load()\n\tif err != nil {\n\t\tt.Fatalf(\"Load: %v\", err)\n\t}\n\n\t// Create and build IR\n\tprog := irutil.CreateProgram(lprog, 0)\n\tp := prog.Package(lprog.Package(\"p\").Pkg)\n\tp.Build()\n\tg := p.Func(\"g\")\n\n\tphis := 0\n\tfor _, b := range g.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tif _, ok := instr.(*ir.Phi); ok {\n\t\t\t\tphis++\n\t\t\t}\n\t\t}\n\t}\n\tif expected := 4; phis != expected {\n\t\tg.WriteTo(os.Stderr)\n\t\tt.Errorf(\"expected %d Phi nodes (for the range index, slice length and slice), got %d\", expected, phis)\n\t}\n}\n\nfunc TestBuildPackageGo120(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tsrc      string\n\t\timporter types.Importer\n\t}{\n\t\t{\"slice to array\", \"package p; var s []byte; var _ = ([4]byte)(s)\", nil},\n\t\t{\"slice to zero length array\", \"package p; var s []byte; var _ = ([0]byte)(s)\", nil},\n\t\t{\"slice to zero length array type parameter\", \"package p; var s []byte; func f[T ~[0]byte]() { tmp := (T)(s); var z T; _ = tmp == z}\", nil},\n\t\t{\"slice to non-zero length array type parameter\", \"package p; var s []byte; func h[T ~[1]byte | [4]byte]() { tmp := T(s); var z T; _ = tmp == z}\", nil},\n\t\t{\"slice to maybe-zero length array type parameter\", \"package p; var s []byte; func g[T ~[0]byte | [4]byte]() { tmp := T(s); var z T; _ = tmp == z}\", nil},\n\t\t{\n\t\t\t\"rune sequence to sequence cast patterns\", `\n\t\t\tpackage p\n\t\t\t// Each of fXX functions describes a 1.20 legal cast between sequences of runes\n\t\t\t// as []rune, pointers to rune arrays, rune arrays, or strings.\n\t\t\t//\n\t\t\t// Comments listed given the current emitted instructions [approximately].\n\t\t\t// If multiple conversions are needed, these are separated by |.\n\t\t\t// rune was selected as it leads to string casts (byte is similar).\n\t\t\t// The length 2 is not significant.\n\t\t\t// Multiple array lengths may occur in a cast in practice (including 0).\n\t\t\tfunc f00[S string, D string](s S)                               { _ = D(s) } // ChangeType\n\t\t\tfunc f01[S string, D []rune](s S)                               { _ = D(s) } // Convert\n\t\t\tfunc f02[S string, D []rune | string](s S)                      { _ = D(s) } // ChangeType | Convert\n\t\t\tfunc f03[S [2]rune, D [2]rune](s S)                             { _ = D(s) } // ChangeType\n\t\t\tfunc f04[S *[2]rune, D *[2]rune](s S)                           { _ = D(s) } // ChangeType\n\t\t\tfunc f05[S []rune, D string](s S)                               { _ = D(s) } // Convert\n\t\t\tfunc f06[S []rune, D [2]rune](s S)                              { _ = D(s) } // SliceToArray\n\t\t\tfunc f07[S []rune, D [2]rune | string](s S)                     { _ = D(s) } // SliceToArray | Convert\n\t\t\tfunc f08[S []rune, D *[2]rune](s S)                             { _ = D(s) } // SliceToArrayPointer\n\t\t\tfunc f09[S []rune, D *[2]rune | string](s S)                    { _ = D(s) } // SliceToArray | Convert\n\t\t\tfunc f10[S []rune, D *[2]rune | [2]rune](s S)                   { _ = D(s) } // SliceToArrayPointer | SliceToArray\n\t\t\tfunc f11[S []rune, D *[2]rune | [2]rune | string](s S)          { _ = D(s) } // SliceToArrayPointer | SliceToArray | Convert\n\t\t\tfunc f12[S []rune, D []rune](s S)                               { _ = D(s) } // ChangeType\n\t\t\tfunc f13[S []rune, D []rune | string](s S)                      { _ = D(s) } // Convert | ChangeType\n\t\t\tfunc f14[S []rune, D []rune | [2]rune](s S)                     { _ = D(s) } // ChangeType | SliceToArray\n\t\t\tfunc f15[S []rune, D []rune | [2]rune | string](s S)            { _ = D(s) } // ChangeType | SliceToArray | Convert\n\t\t\tfunc f16[S []rune, D []rune | *[2]rune](s S)                    { _ = D(s) } // ChangeType | SliceToArrayPointer\n\t\t\tfunc f17[S []rune, D []rune | *[2]rune | string](s S)           { _ = D(s) } // ChangeType | SliceToArrayPointer | Convert\n\t\t\tfunc f18[S []rune, D []rune | *[2]rune | [2]rune](s S)          { _ = D(s) } // ChangeType | SliceToArrayPointer | SliceToArray\n\t\t\tfunc f19[S []rune, D []rune | *[2]rune | [2]rune | string](s S) { _ = D(s) } // ChangeType | SliceToArrayPointer | SliceToArray | Convert\n\t\t\tfunc f20[S []rune | string, D string](s S)                      { _ = D(s) } // Convert | ChangeType\n\t\t\tfunc f21[S []rune | string, D []rune](s S)                      { _ = D(s) } // Convert | ChangeType\n\t\t\tfunc f22[S []rune | string, D []rune | string](s S)             { _ = D(s) } // ChangeType | Convert | Convert | ChangeType\n\t\t\tfunc f23[S []rune | [2]rune, D [2]rune](s S)                    { _ = D(s) } // SliceToArray | ChangeType\n\t\t\tfunc f24[S []rune | *[2]rune, D *[2]rune](s S)                  { _ = D(s) } // SliceToArrayPointer | ChangeType\n\t\t\t`, nil,\n\t\t},\n\t\t{\n\t\t\t\"matching named and underlying types\", `\n\t\t\tpackage p\n\t\t\ttype a string\n\t\t\ttype b string\n\t\t\tfunc g0[S []rune | a | b, D []rune | a | b](s S)      { _ = D(s) }\n\t\t\tfunc g1[S []rune | ~string, D []rune | a | b](s S)    { _ = D(s) }\n\t\t\tfunc g2[S []rune | a | b, D []rune | ~string](s S)    { _ = D(s) }\n\t\t\tfunc g3[S []rune | ~string, D []rune |~string](s S)   { _ = D(s) }\n\t\t\t`, nil,\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tfset := token.NewFileSet()\n\t\t\tf, err := parser.ParseFile(fset, \"p.go\", tc.src, 0)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tfiles := []*ast.File{f}\n\n\t\t\tpkg := types.NewPackage(\"p\", \"\")\n\t\t\tconf := &types.Config{Importer: tc.importer}\n\t\t\t_, _, err = irutil.BuildPackage(conf, fset, pkg, files, ir.SanityCheckFunctions)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected error: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGo117Builtins(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tsrc      string\n\t\timporter types.Importer\n\t}{\n\t\t{\"slice to array pointer\", \"package p; var s []byte; var _ = (*[4]byte)(s)\", nil},\n\t\t{\"unsafe slice\", `package p; import \"unsafe\"; var _ = unsafe.Add(nil, 0)`, importer.Default()},\n\t\t{\"unsafe add\", `package p; import \"unsafe\"; var _ = unsafe.Slice((*int)(nil), 0)`, importer.Default()},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tfset := token.NewFileSet()\n\t\t\tf, err := parser.ParseFile(fset, \"p.go\", tc.src, parser.ParseComments|parser.SkipObjectResolution)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tfiles := []*ast.File{f}\n\n\t\t\tpkg := types.NewPackage(\"p\", \"\")\n\t\t\tconf := &types.Config{Importer: tc.importer}\n\t\t\tif _, _, err := irutil.BuildPackage(conf, fset, pkg, files, ir.SanityCheckFunctions); err != nil {\n\t\t\t\tt.Errorf(\"unexpected error: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestLabels just tests that anonymous labels are handled.\nfunc TestLabels(t *testing.T) {\n\ttests := []string{\n\t\t`package main\n                 func main() { _:println(1) }`,\n\t\t`package main\n                 func main() { _:println(1); _:println(2)}`,\n\t}\n\tfor _, test := range tests {\n\t\tconf := loader.Config{Fset: token.NewFileSet()}\n\t\tf, err := parser.ParseFile(conf.Fset, \"<input>\", test, 0)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"parse error: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tconf.CreateFromFiles(\"main\", f)\n\t\tiprog, err := conf.Load()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\t\tprog := irutil.CreateProgram(iprog, ir.BuilderMode(0))\n\t\tpkg := prog.Package(iprog.Created[0].Pkg)\n\t\tpkg.Build()\n\t}\n}\n\nfunc TestUnreachableExit(t *testing.T) {\n\ttests := []string{\n\t\t`\n\t\tpackage pkg\n\n\t\tfunc foo() (err error) {\n\t\t\tif true {\n\t\t\t\tprintln()\n\t\t\t}\n\n\t\t\tprintln()\n\n\t\t\tfor {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t}`,\n\t}\n\tfor _, test := range tests {\n\t\tconf := loader.Config{Fset: token.NewFileSet()}\n\t\tf, err := parser.ParseFile(conf.Fset, \"<input>\", test, 0)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"parse error: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tconf.CreateFromFiles(\"pkg\", f)\n\t\tiprog, err := conf.Load()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\t\tprog := irutil.CreateProgram(iprog, ir.BuilderMode(0))\n\t\tpkg := prog.Package(iprog.Created[0].Pkg)\n\t\tpkg.Build()\n\t}\n}\n"
  },
  {
    "path": "go/ir/const.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file defines the Const SSA value type.\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/types\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"golang.org/x/exp/typeparams\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n)\n\n// NewConst returns a new constant of the specified value and type.\n// val must be valid according to the specification of Const.Value.\nfunc NewConst(val constant.Value, typ types.Type, source ast.Node) *Const {\n\tc := &Const{\n\t\tregister: register{\n\t\t\ttyp: typ,\n\t\t},\n\t\tValue: val,\n\t}\n\tc.setSource(source)\n\treturn c\n}\n\n// intConst returns an 'int' constant that evaluates to i.\n// (i is an int64 in case the host is narrower than the target.)\nfunc intConst(i int64, source ast.Node) *Const {\n\treturn NewConst(constant.MakeInt64(i), tInt, source)\n}\n\n// nilConst returns a nil constant of the specified type, which may\n// be any reference type, including interfaces.\nfunc nilConst(typ types.Type, source ast.Node) *Const {\n\treturn NewConst(nil, typ, source)\n}\n\n// stringConst returns a 'string' constant that evaluates to s.\nfunc stringConst(s string, source ast.Node) *Const {\n\treturn NewConst(constant.MakeString(s), tString, source)\n}\n\n// zeroConst returns a new \"zero\" constant of the specified type.\nfunc zeroConst(t types.Type, source ast.Node) Constant {\n\tif _, ok := t.Underlying().(*types.Interface); ok && !typeparams.IsTypeParam(t) {\n\t\t// Handle non-generic interface early to simplify following code.\n\t\treturn nilConst(t, source)\n\t}\n\n\ttset := typeutil.NewTypeSet(t)\n\n\tswitch typ := tset.CoreType().(type) {\n\tcase *types.Struct:\n\t\tvalues := make([]Value, typ.NumFields())\n\t\tfor i := 0; i < typ.NumFields(); i++ {\n\t\t\tvalues[i] = zeroConst(typ.Field(i).Type(), source)\n\t\t}\n\t\tac := &AggregateConst{\n\t\t\tregister: register{typ: t},\n\t\t\tValues:   values,\n\t\t}\n\t\tac.setSource(source)\n\t\treturn ac\n\tcase *types.Tuple:\n\t\tvalues := make([]Value, typ.Len())\n\t\tfor i := 0; i < typ.Len(); i++ {\n\t\t\tvalues[i] = zeroConst(typ.At(i).Type(), source)\n\t\t}\n\t\tac := &AggregateConst{\n\t\t\tregister: register{typ: t},\n\t\t\tValues:   values,\n\t\t}\n\t\tac.setSource(source)\n\t\treturn ac\n\t}\n\n\tisNillable := func(term *types.Term) bool {\n\t\tswitch typ := term.Type().Underlying().(type) {\n\t\tcase *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature, *typeutil.Iterator:\n\t\t\treturn true\n\t\tcase *types.Basic:\n\t\t\tswitch typ.Kind() {\n\t\t\tcase types.UnsafePointer, types.UntypedNil:\n\t\t\t\treturn true\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\t}\n\n\tisInfo := func(info types.BasicInfo) func(*types.Term) bool {\n\t\treturn func(term *types.Term) bool {\n\t\t\tbasic, ok := term.Type().Underlying().(*types.Basic)\n\t\t\tif !ok {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn (basic.Info() & info) != 0\n\t\t}\n\t}\n\n\tisArray := func(term *types.Term) bool {\n\t\t_, ok := term.Type().Underlying().(*types.Array)\n\t\treturn ok\n\t}\n\n\tswitch {\n\tcase tset.All(isInfo(types.IsNumeric)):\n\t\treturn NewConst(constant.MakeInt64(0), t, source)\n\tcase tset.All(isInfo(types.IsString)):\n\t\treturn NewConst(constant.MakeString(\"\"), t, source)\n\tcase tset.All(isInfo(types.IsBoolean)):\n\t\treturn NewConst(constant.MakeBool(false), t, source)\n\tcase tset.All(isNillable):\n\t\treturn nilConst(t, source)\n\tcase tset.All(isArray):\n\t\tvar k ArrayConst\n\t\tk.setType(t)\n\t\tk.setSource(source)\n\t\treturn &k\n\tdefault:\n\t\tvar k GenericConst\n\t\tk.setType(t)\n\t\tk.setSource(source)\n\t\treturn &k\n\t}\n}\n\nfunc (c *Const) RelString(from *types.Package) string {\n\tvar p string\n\tif c.Value == nil {\n\t\tp = \"nil\"\n\t} else if c.Value.Kind() == constant.String {\n\t\tv := constant.StringVal(c.Value)\n\t\tconst max = 20\n\t\t// TODO(adonovan): don't cut a rune in half.\n\t\tif len(v) > max {\n\t\t\tv = v[:max-3] + \"...\" // abbreviate\n\t\t}\n\t\tp = strconv.Quote(v)\n\t} else {\n\t\tp = c.Value.String()\n\t}\n\treturn fmt.Sprintf(\"Const <%s> {%s}\", relType(c.Type(), from), p)\n}\n\nfunc (c *Const) String() string {\n\tif c.block == nil {\n\t\t// Constants don't have a block till late in the compilation process. But we want to print consts during\n\t\t// debugging.\n\t\treturn c.RelString(nil)\n\t}\n\treturn c.RelString(c.Parent().pkg())\n}\n\nfunc (v *ArrayConst) RelString(pkg *types.Package) string {\n\treturn fmt.Sprintf(\"ArrayConst <%s>\", relType(v.Type(), pkg))\n}\n\nfunc (v *ArrayConst) String() string {\n\treturn v.RelString(v.Parent().pkg())\n}\n\nfunc (v *AggregateConst) RelString(pkg *types.Package) string {\n\tvalues := make([]string, len(v.Values))\n\tfor i, v := range v.Values {\n\t\tif v != nil {\n\t\t\tvalues[i] = v.Name()\n\t\t} else {\n\t\t\tvalues[i] = \"nil\"\n\t\t}\n\t}\n\treturn fmt.Sprintf(\"AggregateConst <%s> (%s)\", relType(v.Type(), pkg), strings.Join(values, \", \"))\n}\n\nfunc (v *AggregateConst) String() string {\n\tif v.block == nil {\n\t\treturn v.RelString(nil)\n\t}\n\treturn v.RelString(v.Parent().pkg())\n}\n\nfunc (v *GenericConst) RelString(pkg *types.Package) string {\n\treturn fmt.Sprintf(\"GenericConst <%s>\", relType(v.Type(), pkg))\n}\n\nfunc (v *GenericConst) String() string {\n\treturn v.RelString(v.Parent().pkg())\n}\n\n// IsNil returns true if this constant represents a typed or untyped nil value.\nfunc (c *Const) IsNil() bool {\n\treturn c.Value == nil\n}\n\n// Int64 returns the numeric value of this constant truncated to fit\n// a signed 64-bit integer.\nfunc (c *Const) Int64() int64 {\n\tswitch x := constant.ToInt(c.Value); x.Kind() {\n\tcase constant.Int:\n\t\tif i, ok := constant.Int64Val(x); ok {\n\t\t\treturn i\n\t\t}\n\t\treturn 0\n\tcase constant.Float:\n\t\tf, _ := constant.Float64Val(x)\n\t\treturn int64(f)\n\t}\n\tpanic(fmt.Sprintf(\"unexpected constant value: %T\", c.Value))\n}\n\n// Uint64 returns the numeric value of this constant truncated to fit\n// an unsigned 64-bit integer.\nfunc (c *Const) Uint64() uint64 {\n\tswitch x := constant.ToInt(c.Value); x.Kind() {\n\tcase constant.Int:\n\t\tif u, ok := constant.Uint64Val(x); ok {\n\t\t\treturn u\n\t\t}\n\t\treturn 0\n\tcase constant.Float:\n\t\tf, _ := constant.Float64Val(x)\n\t\treturn uint64(f)\n\t}\n\tpanic(fmt.Sprintf(\"unexpected constant value: %T\", c.Value))\n}\n\n// Float64 returns the numeric value of this constant truncated to fit\n// a float64.\nfunc (c *Const) Float64() float64 {\n\tf, _ := constant.Float64Val(c.Value)\n\treturn f\n}\n\n// Complex128 returns the complex value of this constant truncated to\n// fit a complex128.\nfunc (c *Const) Complex128() complex128 {\n\tre, _ := constant.Float64Val(constant.Real(c.Value))\n\tim, _ := constant.Float64Val(constant.Imag(c.Value))\n\treturn complex(re, im)\n}\n\nfunc (c *Const) equal(o Constant) bool {\n\t// TODO(dh): don't use == for types, this will miss identical pointer types, among others\n\toc, ok := o.(*Const)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn c.typ == oc.typ && c.Value == oc.Value && c.source == oc.source\n}\n\nfunc (c *AggregateConst) equal(o Constant) bool {\n\toc, ok := o.(*AggregateConst)\n\tif !ok {\n\t\treturn false\n\t}\n\t// TODO(dh): don't use == for types, this will miss identical pointer types, among others\n\tif c.typ != oc.typ {\n\t\treturn false\n\t}\n\tif c.source != oc.source {\n\t\treturn false\n\t}\n\tfor i, v := range c.Values {\n\t\tif !v.(Constant).equal(oc.Values[i].(Constant)) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (c *ArrayConst) equal(o Constant) bool {\n\toc, ok := o.(*ArrayConst)\n\tif !ok {\n\t\treturn false\n\t}\n\t// TODO(dh): don't use == for types, this will miss identical pointer types, among others\n\treturn c.typ == oc.typ && c.source == oc.source\n}\n\nfunc (c *GenericConst) equal(o Constant) bool {\n\toc, ok := o.(*GenericConst)\n\tif !ok {\n\t\treturn false\n\t}\n\t// TODO(dh): don't use == for types, this will miss identical pointer types, among others\n\treturn c.typ == oc.typ && c.source == oc.source\n}\n"
  },
  {
    "path": "go/ir/create.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file implements the CREATE phase of IR construction.\n// See builder.go for explanation.\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"go/version\"\n\t\"os\"\n\t\"sync\"\n\n\t\"honnef.co/go/tools/go/types/typeutil\"\n)\n\n// measured on the standard library and rounded up to powers of two,\n// on average there are 8 blocks and 16 instructions per block in a\n// function.\nconst avgBlocks = 8\nconst avgInstructionsPerBlock = 16\n\n// NewProgram returns a new IR Program.\n//\n// mode controls diagnostics and checking during IR construction.\nfunc NewProgram(fset *token.FileSet, mode BuilderMode) *Program {\n\tprog := &Program{\n\t\tFset:     fset,\n\t\timported: make(map[string]*Package),\n\t\tpackages: make(map[*types.Package]*Package),\n\t\tmode:     mode,\n\t}\n\n\th := typeutil.MakeHasher() // protected by methodsMu, in effect\n\tprog.methodSets.SetHasher(h)\n\tprog.canon.SetHasher(h)\n\n\treturn prog\n}\n\n// memberFromObject populates package pkg with a member for the\n// typechecker object obj.\n//\n// For objects from Go source code, syntax is the associated syntax tree\n// (for funcs and vars only) and goversion defines the appropriate\n// interpretation; they will be used during the build phase.\nfunc memberFromObject(pkg *Package, obj types.Object, syntax ast.Node, goversion string) {\n\tname := obj.Name()\n\tswitch obj := obj.(type) {\n\tcase *types.Builtin:\n\t\tif pkg.Pkg != types.Unsafe {\n\t\t\tpanic(\"unexpected builtin object: \" + obj.String())\n\t\t}\n\n\tcase *types.TypeName:\n\t\tif name != \"_\" {\n\t\t\tpkg.Members[name] = &Type{\n\t\t\t\tobject: obj,\n\t\t\t\tpkg:    pkg,\n\t\t\t}\n\t\t}\n\n\tcase *types.Const:\n\t\tc := &NamedConst{\n\t\t\tobject: obj,\n\t\t\tValue:  NewConst(obj.Val(), obj.Type(), syntax),\n\t\t\tpkg:    pkg,\n\t\t}\n\t\tpkg.values[obj] = c.Value\n\t\tif name != \"_\" {\n\t\t\tpkg.Members[name] = c\n\t\t}\n\n\tcase *types.Var:\n\t\tg := &Global{\n\t\t\tPkg:    pkg,\n\t\t\tname:   name,\n\t\t\tobject: obj,\n\t\t\ttyp:    types.NewPointer(obj.Type()), // address\n\t\t}\n\t\tpkg.values[obj] = g\n\t\tif name != \"_\" {\n\t\t\tpkg.Members[name] = g\n\t\t}\n\n\tcase *types.Func:\n\t\tsig := obj.Type().(*types.Signature)\n\t\tif sig.Recv() == nil && name == \"init\" {\n\t\t\tpkg.ninit++\n\t\t\tname = fmt.Sprintf(\"init#%d\", pkg.ninit)\n\t\t}\n\t\tfn := &Function{\n\t\t\tname:      name,\n\t\t\tobject:    obj,\n\t\t\tSignature: sig,\n\t\t\tPkg:       pkg,\n\t\t\tProg:      pkg.Prog,\n\t\t\tgoversion: goversion,\n\t\t}\n\n\t\tfn.source = syntax\n\t\tfn.initHTML(pkg.printFunc)\n\t\tif syntax == nil {\n\t\t\tfn.Synthetic = SyntheticLoadedFromExportData\n\t\t} else {\n\t\t\t// Note: we initialize fn.Blocks in\n\t\t\t// (*builder).buildFunction and not here because Blocks\n\t\t\t// being nil is used to indicate that building of the\n\t\t\t// function hasn't started yet.\n\n\t\t\tfn.functionBody = &functionBody{\n\t\t\t\tscratchInstructions: make([]Instruction, avgBlocks*avgInstructionsPerBlock),\n\t\t\t}\n\t\t}\n\n\t\tpkg.values[obj] = fn\n\t\tpkg.Functions = append(pkg.Functions, fn)\n\t\tif name != \"_\" && sig.Recv() == nil {\n\t\t\tpkg.Members[name] = fn // package-level function\n\t\t}\n\n\tdefault: // (incl. *types.Package)\n\t\tpanic(\"unexpected Object type: \" + obj.String())\n\t}\n}\n\n// membersFromDecl populates package pkg with members for each\n// typechecker object (var, func, const or type) associated with the\n// specified decl.\nfunc membersFromDecl(pkg *Package, decl ast.Decl, goversion string) {\n\tswitch decl := decl.(type) {\n\tcase *ast.GenDecl: // import, const, type or var\n\t\tswitch decl.Tok {\n\t\tcase token.CONST:\n\t\t\tfor _, spec := range decl.Specs {\n\t\t\t\tfor _, id := range spec.(*ast.ValueSpec).Names {\n\t\t\t\t\tmemberFromObject(pkg, pkg.info.Defs[id], nil, \"\")\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase token.VAR:\n\t\t\tfor _, spec := range decl.Specs {\n\t\t\t\tfor _, rhs := range spec.(*ast.ValueSpec).Values {\n\t\t\t\t\tpkg.initVersion[rhs] = goversion\n\t\t\t\t}\n\t\t\t\tfor _, id := range spec.(*ast.ValueSpec).Names {\n\t\t\t\t\tmemberFromObject(pkg, pkg.info.Defs[id], spec, goversion)\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase token.TYPE:\n\t\t\tfor _, spec := range decl.Specs {\n\t\t\t\tid := spec.(*ast.TypeSpec).Name\n\t\t\t\tmemberFromObject(pkg, pkg.info.Defs[id], nil, \"\")\n\t\t\t}\n\t\t}\n\n\tcase *ast.FuncDecl:\n\t\tid := decl.Name\n\t\tobj, ok := pkg.info.Defs[id]\n\t\tif !ok {\n\t\t\tpanic(fmt.Sprintf(\"couldn't find object for id %q at %s\",\n\t\t\t\tid.Name, pkg.Prog.Fset.PositionFor(id.Pos(), false)))\n\t\t}\n\t\tif obj == nil {\n\t\t\tpanic(fmt.Sprintf(\"found nil object for id %q at %s\",\n\t\t\t\tid.Name, pkg.Prog.Fset.PositionFor(id.Pos(), false)))\n\t\t}\n\t\tmemberFromObject(pkg, obj, decl, goversion)\n\t}\n}\n\n// CreatePackage constructs and returns an IR Package from the\n// specified type-checked, error-free file ASTs, and populates its\n// Members mapping.\n//\n// importable determines whether this package should be returned by a\n// subsequent call to ImportedPackage(pkg.Path()).\n//\n// The real work of building IR form for each function is not done\n// until a subsequent call to Package.Build().\nfunc (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {\n\tp := &Package{\n\t\tProg:    prog,\n\t\tMembers: make(map[string]Member),\n\t\tvalues:  make(map[types.Object]Value),\n\t\tPkg:     pkg,\n\t\t// transient values (CREATE and BUILD phases)\n\t\tinfo:        info,\n\t\tfiles:       files,\n\t\tprintFunc:   prog.PrintFunc,\n\t\tinitVersion: make(map[ast.Expr]string),\n\t}\n\n\t// Add init() function.\n\tp.init = &Function{\n\t\tname:         \"init\",\n\t\tSignature:    new(types.Signature),\n\t\tSynthetic:    SyntheticPackageInitializer,\n\t\tPkg:          p,\n\t\tProg:         prog,\n\t\tfunctionBody: new(functionBody),\n\t\tgoversion:    \"\", // See Package.build for details.\n\t}\n\tp.init.initHTML(prog.PrintFunc)\n\tp.Members[p.init.name] = p.init\n\tp.Functions = append(p.Functions, p.init)\n\n\t// CREATE phase.\n\t// Allocate all package members: vars, funcs, consts and types.\n\tif len(files) > 0 {\n\t\t// Go source package.\n\t\tfor _, file := range files {\n\t\t\tgoversion := version.Lang(p.info.FileVersions[file])\n\t\t\tfor _, decl := range file.Decls {\n\t\t\t\tmembersFromDecl(p, decl, goversion)\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// GC-compiled binary package (or \"unsafe\")\n\t\t// No code.\n\t\t// No position information.\n\t\tscope := p.Pkg.Scope()\n\t\tfor _, name := range scope.Names() {\n\t\t\tobj := scope.Lookup(name)\n\t\t\tmemberFromObject(p, obj, nil, \"\")\n\t\t\tif obj, ok := obj.(*types.TypeName); ok {\n\t\t\t\tif named, ok := obj.Type().(*types.Named); ok {\n\t\t\t\t\tfor i, n := 0, named.NumMethods(); i < n; i++ {\n\t\t\t\t\t\tmemberFromObject(p, named.Method(i), nil, \"\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Add initializer guard variable.\n\tinitguard := &Global{\n\t\tPkg:  p,\n\t\tname: \"init$guard\",\n\t\ttyp:  types.NewPointer(tBool),\n\t}\n\tp.Members[initguard.Name()] = initguard\n\n\tif prog.mode&GlobalDebug != 0 {\n\t\tp.SetDebugMode(true)\n\t}\n\n\tif prog.mode&PrintPackages != 0 {\n\t\tprintMu.Lock()\n\t\tp.WriteTo(os.Stdout)\n\t\tprintMu.Unlock()\n\t}\n\n\tif importable {\n\t\tprog.imported[p.Pkg.Path()] = p\n\t}\n\tprog.packages[p.Pkg] = p\n\n\treturn p\n}\n\n// printMu serializes printing of Packages/Functions to stdout.\nvar printMu sync.Mutex\n\n// AllPackages returns a new slice containing all packages in the\n// program prog in unspecified order.\nfunc (prog *Program) AllPackages() []*Package {\n\tpkgs := make([]*Package, 0, len(prog.packages))\n\tfor _, pkg := range prog.packages {\n\t\tpkgs = append(pkgs, pkg)\n\t}\n\treturn pkgs\n}\n\n// ImportedPackage returns the importable Package whose PkgPath\n// is path, or nil if no such Package has been created.\n//\n// A parameter to CreatePackage determines whether a package should be\n// considered importable. For example, no import declaration can resolve\n// to the ad-hoc main package created by 'go build foo.go'.\n//\n// TODO(adonovan): rethink this function and the \"importable\" concept;\n// most packages are importable. This function assumes that all\n// types.Package.Path values are unique within the ir.Program, which is\n// false---yet this function remains very convenient.\n// Clients should use (*Program).Package instead where possible.\n// IR doesn't really need a string-keyed map of packages.\nfunc (prog *Program) ImportedPackage(path string) *Package {\n\treturn prog.imported[path]\n}\n\nfunc (prog *Program) SetNoReturn(fn func(*types.Func) bool) {\n\tprog.noReturn = fn\n}\n"
  },
  {
    "path": "go/ir/doc.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package ir defines a representation of the elements of Go programs\n// (packages, types, functions, variables and constants) using a\n// static single-information (SSI) form intermediate representation\n// (IR) for the bodies of functions.\n//\n// THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE.\n//\n// For an introduction to SSA form, upon which SSI builds, see\n// https://en.wikipedia.org/wiki/Static_single_assignment_form.\n// This page provides a broader reading list:\n// https://www.dcs.gla.ac.uk/~jsinger/ssa.html.\n//\n// For an introduction to SSI form, see The static single information\n// form by C. Scott Ananian.\n//\n// The level of abstraction of the IR form is intentionally close to\n// the source language to facilitate construction of source analysis\n// tools.  It is not intended for machine code generation.\n//\n// The simplest way to create the IR of a package is\n// to load typed syntax trees using golang.org/x/tools/go/packages, then\n// invoke the irutil.Packages helper function. See ExampleLoadPackages\n// and ExampleWholeProgram for examples.\n// The resulting ir.Program contains all the packages and their\n// members, but IR code is not created for function bodies until a\n// subsequent call to (*Package).Build or (*Program).Build.\n//\n// The builder initially builds a naive IR form in which all local\n// variables are addresses of stack locations with explicit loads and\n// stores.  Registerization of eligible locals and φ-node insertion\n// using dominance and dataflow are then performed as a second pass\n// called \"lifting\" to improve the accuracy and performance of\n// subsequent analyses; this pass can be skipped by setting the\n// NaiveForm builder flag.\n//\n// The primary interfaces of this package are:\n//\n//   - Member: a named member of a Go package.\n//   - Value: an expression that yields a value.\n//   - Instruction: a statement that consumes values and performs computation.\n//   - Node: a Value or Instruction (emphasizing its membership in the IR value graph)\n//\n// A computation that yields a result implements both the Value and\n// Instruction interfaces.  The following table shows for each\n// concrete type which of these interfaces it implements.\n//\n//\t                   Value?          Instruction?    Member?\n//\t*Alloc                ✔               ✔\n//\t*BinOp                ✔               ✔\n//\t*BlankStore                           ✔\n//\t*Builtin              ✔\n//\t*Call                 ✔               ✔\n//\t*ChangeInterface      ✔               ✔\n//\t*ChangeType           ✔               ✔\n//\t*Const                ✔               ✔\n//\t*Convert              ✔               ✔\n//\t*DebugRef                             ✔\n//\t*Defer                ✔               ✔\n//\t*Extract              ✔               ✔\n//\t*Field                ✔               ✔\n//\t*FieldAddr            ✔               ✔\n//\t*FreeVar              ✔\n//\t*Function             ✔                               ✔ (func)\n//\t*Global               ✔                               ✔ (var)\n//\t*Go                   ✔               ✔\n//\t*If                                   ✔\n//\t*Index                ✔               ✔\n//\t*IndexAddr            ✔               ✔\n//\t*Jump                                 ✔\n//\t*Load                 ✔               ✔\n//\t*MakeChan             ✔               ✔\n//\t*MakeClosure          ✔               ✔\n//\t*MakeInterface        ✔               ✔\n//\t*MakeMap              ✔               ✔\n//\t*MakeSlice            ✔               ✔\n//\t*MapLookup            ✔               ✔\n//\t*MapUpdate            ✔               ✔\n//\t*MultiConvert         ✔               ✔\n//\t*NamedConst                                           ✔ (const)\n//\t*Next                 ✔               ✔\n//\t*Panic                                ✔\n//\t*Parameter            ✔               ✔\n//\t*Phi                  ✔               ✔\n//\t*Range                ✔               ✔\n//\t*Recv                 ✔               ✔\n//\t*Return                               ✔\n//\t*RunDefers                            ✔\n//\t*Select               ✔               ✔\n//\t*Send                 ✔               ✔\n//\t*Sigma                ✔               ✔\n//\t*Slice                ✔               ✔\n//\t*SliceToArrayPointer  ✔               ✔\n//\t*SliceToArray         ✔               ✔\n//\t*Store                ✔               ✔\n//\t*StringLookup         ✔               ✔\n//\t*Type                                                 ✔ (type)\n//\t*TypeAssert           ✔               ✔\n//\t*UnOp                 ✔               ✔\n//\t*Unreachable                          ✔\n//\n// Other key types in this package include: Program, Package, Function\n// and BasicBlock.\n//\n// The program representation constructed by this package is fully\n// resolved internally, i.e. it does not rely on the names of Values,\n// Packages, Functions, Types or BasicBlocks for the correct\n// interpretation of the program.  Only the identities of objects and\n// the topology of the IR and type graphs are semantically\n// significant.  (There is one exception: Ids, used to identify field\n// and method names, contain strings.)  Avoidance of name-based\n// operations simplifies the implementation of subsequent passes and\n// can make them very efficient.  Many objects are nonetheless named\n// to aid in debugging, but it is not essential that the names be\n// either accurate or unambiguous.  The public API exposes a number of\n// name-based maps for client convenience.\n//\n// The ir/irutil package provides various utilities that depend only\n// on the public API of this package.\n//\n// TODO(adonovan): Consider the exceptional control-flow implications\n// of defer and recover().\n//\n// TODO(adonovan): write a how-to document for all the various cases\n// of trying to determine corresponding elements across the four\n// domains of source locations, ast.Nodes, types.Objects,\n// ir.Values/Instructions.\npackage ir\n"
  },
  {
    "path": "go/ir/dom.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file defines algorithms related to dominance.\n\n// Dominator tree construction ----------------------------------------\n//\n// We use the algorithm described in Lengauer & Tarjan. 1979.  A fast\n// algorithm for finding dominators in a flowgraph.\n// https://doi.acm.org/10.1145/357062.357071\n//\n// We also apply the optimizations to SLT described in Georgiadis et\n// al, Finding Dominators in Practice, JGAA 2006,\n// https://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf\n// to avoid the need for buckets of size > 1.\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/big\"\n\t\"os\"\n\t\"sort\"\n)\n\n// Idom returns the block that immediately dominates b:\n// its parent in the dominator tree, if any.\n// The entry node (b.Index==0) does not have a parent.\nfunc (b *BasicBlock) Idom() *BasicBlock { return b.dom.idom }\n\n// Dominees returns the list of blocks that b immediately dominates:\n// its children in the dominator tree.\nfunc (b *BasicBlock) Dominees() []*BasicBlock { return b.dom.children }\n\n// Dominates reports whether b dominates c.\nfunc (b *BasicBlock) Dominates(c *BasicBlock) bool {\n\treturn b.dom.pre <= c.dom.pre && c.dom.post <= b.dom.post\n}\n\ntype byDomPreorder []*BasicBlock\n\nfunc (a byDomPreorder) Len() int           { return len(a) }\nfunc (a byDomPreorder) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }\nfunc (a byDomPreorder) Less(i, j int) bool { return a[i].dom.pre < a[j].dom.pre }\n\n// DomPreorder returns a new slice containing the blocks of f in\n// dominator tree preorder.\nfunc (f *Function) DomPreorder() []*BasicBlock {\n\tn := len(f.Blocks)\n\torder := make(byDomPreorder, n)\n\tcopy(order, f.Blocks)\n\tsort.Sort(order)\n\treturn order\n}\n\n// domInfo contains a BasicBlock's dominance information.\ntype domInfo struct {\n\tidom      *BasicBlock   // immediate dominator (parent in domtree)\n\tchildren  []*BasicBlock // nodes immediately dominated by this one\n\tpre, post int32         // pre- and post-order numbering within domtree\n}\n\n// buildDomTree computes the dominator tree of f using the LT algorithm.\n// Precondition: all blocks are reachable (e.g. optimizeBlocks has been run).\nfunc buildDomTree(fn *Function) {\n\t// The step numbers refer to the original LT paper; the\n\t// reordering is due to Georgiadis.\n\n\t// Clear any previous domInfo.\n\tfor _, b := range fn.Blocks {\n\t\tb.dom = domInfo{}\n\t}\n\n\tidoms := make([]*BasicBlock, len(fn.Blocks))\n\n\torder := make([]*BasicBlock, 0, len(fn.Blocks))\n\tseen := fn.blockset(0)\n\tvar dfs func(b *BasicBlock)\n\tdfs = func(b *BasicBlock) {\n\t\tif !seen.Add(b) {\n\t\t\treturn\n\t\t}\n\t\tfor _, succ := range b.Succs {\n\t\t\tdfs(succ)\n\t\t}\n\t\tif fn.fakeExits.Has(b) {\n\t\t\tdfs(fn.Exit)\n\t\t}\n\t\torder = append(order, b)\n\t\tb.post = len(order) - 1\n\t}\n\tdfs(fn.Blocks[0])\n\n\tfor i := 0; i < len(order)/2; i++ {\n\t\to := len(order) - i - 1\n\t\torder[i], order[o] = order[o], order[i]\n\t}\n\n\tidoms[fn.Blocks[0].Index] = fn.Blocks[0]\n\tchanged := true\n\tfor changed {\n\t\tchanged = false\n\t\t// iterate over all nodes in reverse postorder, except for the\n\t\t// entry node\n\t\tfor _, b := range order[1:] {\n\t\t\tvar newIdom *BasicBlock\n\t\t\tdo := func(p *BasicBlock) {\n\t\t\t\tif idoms[p.Index] == nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif newIdom == nil {\n\t\t\t\t\tnewIdom = p\n\t\t\t\t} else {\n\t\t\t\t\tfinger1 := p\n\t\t\t\t\tfinger2 := newIdom\n\t\t\t\t\tfor finger1 != finger2 {\n\t\t\t\t\t\tfor finger1.post < finger2.post {\n\t\t\t\t\t\t\tfinger1 = idoms[finger1.Index]\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor finger2.post < finger1.post {\n\t\t\t\t\t\t\tfinger2 = idoms[finger2.Index]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tnewIdom = finger1\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor _, p := range b.Preds {\n\t\t\t\tdo(p)\n\t\t\t}\n\t\t\tif b == fn.Exit {\n\t\t\t\tfor _, p := range fn.Blocks {\n\t\t\t\t\tif fn.fakeExits.Has(p) {\n\t\t\t\t\t\tdo(p)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif idoms[b.Index] != newIdom {\n\t\t\t\tidoms[b.Index] = newIdom\n\t\t\t\tchanged = true\n\t\t\t}\n\t\t}\n\t}\n\n\tfor i, b := range idoms {\n\t\tfn.Blocks[i].dom.idom = b\n\t\tif b == nil {\n\t\t\t// malformed CFG\n\t\t\tcontinue\n\t\t}\n\t\tif i == b.Index {\n\t\t\tcontinue\n\t\t}\n\t\tb.dom.children = append(b.dom.children, fn.Blocks[i])\n\t}\n\n\tnumberDomTree(fn.Blocks[0], 0, 0)\n\n\t// printDomTreeDot(os.Stderr, fn) // debugging\n\t// printDomTreeText(os.Stderr, root, 0) // debugging\n\n\tif fn.Prog.mode&SanityCheckFunctions != 0 {\n\t\tsanityCheckDomTree(fn)\n\t}\n}\n\n// buildPostDomTree is like buildDomTree, but builds the post-dominator tree instead.\nfunc buildPostDomTree(fn *Function) {\n\t// The step numbers refer to the original LT paper; the\n\t// reordering is due to Georgiadis.\n\n\t// Clear any previous domInfo.\n\tfor _, b := range fn.Blocks {\n\t\tb.pdom = domInfo{}\n\t}\n\n\tidoms := make([]*BasicBlock, len(fn.Blocks))\n\n\torder := make([]*BasicBlock, 0, len(fn.Blocks))\n\tseen := fn.blockset(0)\n\tvar dfs func(b *BasicBlock)\n\tdfs = func(b *BasicBlock) {\n\t\tif !seen.Add(b) {\n\t\t\treturn\n\t\t}\n\t\tfor _, pred := range b.Preds {\n\t\t\tdfs(pred)\n\t\t}\n\t\tif b == fn.Exit {\n\t\t\tfor _, p := range fn.Blocks {\n\t\t\t\tif fn.fakeExits.Has(p) {\n\t\t\t\t\tdfs(p)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\torder = append(order, b)\n\t\tb.post = len(order) - 1\n\t}\n\tdfs(fn.Exit)\n\n\tfor i := 0; i < len(order)/2; i++ {\n\t\to := len(order) - i - 1\n\t\torder[i], order[o] = order[o], order[i]\n\t}\n\n\tidoms[fn.Exit.Index] = fn.Exit\n\tchanged := true\n\tfor changed {\n\t\tchanged = false\n\t\t// iterate over all nodes in reverse postorder, except for the\n\t\t// exit node\n\t\tfor _, b := range order[1:] {\n\t\t\tvar newIdom *BasicBlock\n\t\t\tdo := func(p *BasicBlock) {\n\t\t\t\tif idoms[p.Index] == nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif newIdom == nil {\n\t\t\t\t\tnewIdom = p\n\t\t\t\t} else {\n\t\t\t\t\tfinger1 := p\n\t\t\t\t\tfinger2 := newIdom\n\t\t\t\t\tfor finger1 != finger2 {\n\t\t\t\t\t\tfor finger1.post < finger2.post {\n\t\t\t\t\t\t\tfinger1 = idoms[finger1.Index]\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor finger2.post < finger1.post {\n\t\t\t\t\t\t\tfinger2 = idoms[finger2.Index]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tnewIdom = finger1\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor _, p := range b.Succs {\n\t\t\t\tdo(p)\n\t\t\t}\n\t\t\tif fn.fakeExits.Has(b) {\n\t\t\t\tdo(fn.Exit)\n\t\t\t}\n\n\t\t\tif idoms[b.Index] != newIdom {\n\t\t\t\tidoms[b.Index] = newIdom\n\t\t\t\tchanged = true\n\t\t\t}\n\t\t}\n\t}\n\n\tfor i, b := range idoms {\n\t\tfn.Blocks[i].pdom.idom = b\n\t\tif b == nil {\n\t\t\t// malformed CFG\n\t\t\tcontinue\n\t\t}\n\t\tif i == b.Index {\n\t\t\tcontinue\n\t\t}\n\t\tb.pdom.children = append(b.pdom.children, fn.Blocks[i])\n\t}\n\n\tnumberPostDomTree(fn.Exit, 0, 0)\n\n\t// printPostDomTreeDot(os.Stderr, fn) // debugging\n\t// printPostDomTreeText(os.Stderr, fn.Exit, 0) // debugging\n\n\tif fn.Prog.mode&SanityCheckFunctions != 0 { // XXX\n\t\tsanityCheckDomTree(fn) // XXX\n\t}\n}\n\n// numberDomTree sets the pre- and post-order numbers of a depth-first\n// traversal of the dominator tree rooted at v.  These are used to\n// answer dominance queries in constant time.\nfunc numberDomTree(v *BasicBlock, pre, post int32) (int32, int32) {\n\tv.dom.pre = pre\n\tpre++\n\tfor _, child := range v.dom.children {\n\t\tpre, post = numberDomTree(child, pre, post)\n\t}\n\tv.dom.post = post\n\tpost++\n\treturn pre, post\n}\n\n// numberPostDomTree sets the pre- and post-order numbers of a depth-first\n// traversal of the post-dominator tree rooted at v.  These are used to\n// answer post-dominance queries in constant time.\nfunc numberPostDomTree(v *BasicBlock, pre, post int32) (int32, int32) {\n\tv.pdom.pre = pre\n\tpre++\n\tfor _, child := range v.pdom.children {\n\t\tpre, post = numberPostDomTree(child, pre, post)\n\t}\n\tv.pdom.post = post\n\tpost++\n\treturn pre, post\n}\n\n// Testing utilities ----------------------------------------\n\n// sanityCheckDomTree checks the correctness of the dominator tree\n// computed by the LT algorithm by comparing against the dominance\n// relation computed by a naive Kildall-style forward dataflow\n// analysis (Algorithm 10.16 from the \"Dragon\" book).\nfunc sanityCheckDomTree(f *Function) {\n\tn := len(f.Blocks)\n\n\t// D[i] is the set of blocks that dominate f.Blocks[i],\n\t// represented as a bit-set of block indices.\n\tD := make([]big.Int, n)\n\n\tone := big.NewInt(1)\n\n\t// all is the set of all blocks; constant.\n\tvar all big.Int\n\tall.Set(one).Lsh(&all, uint(n)).Sub(&all, one)\n\n\t// Initialization.\n\tfor i := range f.Blocks {\n\t\tif i == 0 {\n\t\t\t// A root is dominated only by itself.\n\t\t\tD[i].SetBit(&D[0], 0, 1)\n\t\t} else {\n\t\t\t// All other blocks are (initially) dominated\n\t\t\t// by every block.\n\t\t\tD[i].Set(&all)\n\t\t}\n\t}\n\n\t// Iteration until fixed point.\n\tfor changed := true; changed; {\n\t\tchanged = false\n\t\tfor i, b := range f.Blocks {\n\t\t\tif i == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// Compute intersection across predecessors.\n\t\t\tvar x big.Int\n\t\t\tx.Set(&all)\n\t\t\tfor _, pred := range b.Preds {\n\t\t\t\tx.And(&x, &D[pred.Index])\n\t\t\t}\n\t\t\tif b == f.Exit {\n\t\t\t\tfor _, p := range f.Blocks {\n\t\t\t\t\tif f.fakeExits.Has(p) {\n\t\t\t\t\t\tx.And(&x, &D[p.Index])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tx.SetBit(&x, i, 1) // a block always dominates itself.\n\t\t\tif D[i].Cmp(&x) != 0 {\n\t\t\t\tD[i].Set(&x)\n\t\t\t\tchanged = true\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check the entire relation.  O(n^2).\n\tok := true\n\tfor i := range n {\n\t\tfor j := range n {\n\t\t\tb, c := f.Blocks[i], f.Blocks[j]\n\t\t\tactual := b.Dominates(c)\n\t\t\texpected := D[j].Bit(i) == 1\n\t\t\tif actual != expected {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"dominates(%s, %s)==%t, want %t\\n\", b, c, actual, expected)\n\t\t\t\tok = false\n\t\t\t}\n\t\t}\n\t}\n\n\tpreorder := f.DomPreorder()\n\tfor _, b := range f.Blocks {\n\t\tif got := preorder[b.dom.pre]; got != b {\n\t\t\tfmt.Fprintf(os.Stderr, \"preorder[%d]==%s, want %s\\n\", b.dom.pre, got, b)\n\t\t\tok = false\n\t\t}\n\t}\n\n\tif !ok {\n\t\tpanic(\"sanityCheckDomTree failed for \" + f.String())\n\t}\n\n}\n\n// Printing functions ----------------------------------------\n\n// printDomTree prints the dominator tree as text, using indentation.\n//\n//lint:ignore U1000 used during debugging\nfunc printDomTreeText(buf *bytes.Buffer, v *BasicBlock, indent int) {\n\tfmt.Fprintf(buf, \"%*s%s\\n\", 4*indent, \"\", v)\n\tfor _, child := range v.dom.children {\n\t\tprintDomTreeText(buf, child, indent+1)\n\t}\n}\n\n// printDomTreeDot prints the dominator tree of f in AT&T GraphViz\n// (.dot) format.\n//\n//lint:ignore U1000 used during debugging\nfunc printDomTreeDot(buf io.Writer, f *Function) {\n\tfmt.Fprintln(buf, \"//\", f)\n\tfmt.Fprintln(buf, \"digraph domtree {\")\n\tfor i, b := range f.Blocks {\n\t\tv := b.dom\n\t\tfmt.Fprintf(buf, \"\\tn%d [label=\\\"%s (%d, %d)\\\",shape=\\\"rectangle\\\"];\\n\", v.pre, b, v.pre, v.post)\n\t\t// TODO(adonovan): improve appearance of edges\n\t\t// belonging to both dominator tree and CFG.\n\n\t\t// Dominator tree edge.\n\t\tif i != 0 {\n\t\t\tfmt.Fprintf(buf, \"\\tn%d -> n%d [style=\\\"solid\\\",weight=100];\\n\", v.idom.dom.pre, v.pre)\n\t\t}\n\t\t// CFG edges.\n\t\tfor _, pred := range b.Preds {\n\t\t\tfmt.Fprintf(buf, \"\\tn%d -> n%d [style=\\\"dotted\\\",weight=0];\\n\", pred.dom.pre, v.pre)\n\t\t}\n\n\t\tif f.fakeExits.Has(b) {\n\t\t\tfmt.Fprintf(buf, \"\\tn%d -> n%d [style=\\\"dotted\\\",weight=0,color=red];\\n\", b.dom.pre, f.Exit.dom.pre)\n\t\t}\n\t}\n\tfmt.Fprintln(buf, \"}\")\n}\n\n// printDomTree prints the dominator tree as text, using indentation.\n//\n//lint:ignore U1000 used during debugging\nfunc printPostDomTreeText(buf io.Writer, v *BasicBlock, indent int) {\n\tfmt.Fprintf(buf, \"%*s%s\\n\", 4*indent, \"\", v)\n\tfor _, child := range v.pdom.children {\n\t\tprintPostDomTreeText(buf, child, indent+1)\n\t}\n}\n\n// printDomTreeDot prints the dominator tree of f in AT&T GraphViz\n// (.dot) format.\n//\n//lint:ignore U1000 used during debugging\nfunc printPostDomTreeDot(buf io.Writer, f *Function) {\n\tfmt.Fprintln(buf, \"//\", f)\n\tfmt.Fprintln(buf, \"digraph pdomtree {\")\n\tfor _, b := range f.Blocks {\n\t\tv := b.pdom\n\t\tfmt.Fprintf(buf, \"\\tn%d [label=\\\"%s (%d, %d)\\\",shape=\\\"rectangle\\\"];\\n\", v.pre, b, v.pre, v.post)\n\t\t// TODO(adonovan): improve appearance of edges\n\t\t// belonging to both dominator tree and CFG.\n\n\t\t// Dominator tree edge.\n\t\tif b != f.Exit {\n\t\t\tfmt.Fprintf(buf, \"\\tn%d -> n%d [style=\\\"solid\\\",weight=100];\\n\", v.idom.pdom.pre, v.pre)\n\t\t}\n\t\t// CFG edges.\n\t\tfor _, pred := range b.Preds {\n\t\t\tfmt.Fprintf(buf, \"\\tn%d -> n%d [style=\\\"dotted\\\",weight=0];\\n\", pred.pdom.pre, v.pre)\n\t\t}\n\n\t\tif f.fakeExits.Has(b) {\n\t\t\tfmt.Fprintf(buf, \"\\tn%d -> n%d [style=\\\"dotted\\\",weight=0,color=red];\\n\", b.dom.pre, f.Exit.dom.pre)\n\t\t}\n\t}\n\tfmt.Fprintln(buf, \"}\")\n}\n"
  },
  {
    "path": "go/ir/emit.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// Helpers for emitting IR instructions.\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/exp/typeparams\"\n)\n\n// emitAlloc emits to f a new Alloc instruction allocating a variable\n// of type typ.\n//\n// The caller must set Alloc.Heap=true (for a heap-allocated variable)\n// or add the Alloc to f.Locals (for a frame-allocated variable).\n//\n// During building, a variable in f.Locals may have its Heap flag\n// set when it is discovered that its address is taken.\n// These Allocs are removed from f.Locals at the end.\n//\n// The builder should generally call one of the emit{New,Local,LocalVar} wrappers instead.\nfunc emitAlloc(f *Function, typ types.Type, source ast.Node, comment string) *Alloc {\n\tv := &Alloc{}\n\tv.comment = comment\n\tv.setType(types.NewPointer(typ))\n\tf.emit(v, source)\n\treturn v\n}\n\n// emitNew emits to f a new Alloc instruction heap-allocating a\n// variable of type typ.\nfunc emitNew(f *Function, typ types.Type, source ast.Node, comment string) *Alloc {\n\talloc := emitAlloc(f, typ, source, comment)\n\talloc.Heap = true\n\treturn alloc\n}\n\n// emitLocal creates a local var for (t, source, comment) and\n// emits an Alloc instruction for it.\n//\n// (Use this function or emitNew for synthetic variables;\n// for source-level variables, use emitLocalVar.)\nfunc emitLocal(f *Function, t types.Type, source ast.Node, comment string) *Alloc {\n\tlocal := emitAlloc(f, t, source, comment)\n\tf.Locals = append(f.Locals, local)\n\treturn local\n}\n\n// emitLocalVar creates a local var for v and emits an Alloc instruction for it.\n// Subsequent calls to f.lookup(v) return it.\nfunc emitLocalVar(f *Function, v *types.Var, source ast.Node) *Alloc {\n\talloc := emitLocal(f, v.Type(), source, v.Name())\n\tf.vars[v] = alloc\n\treturn alloc\n}\n\n// emitLoad emits to f an instruction to load the address addr into a\n// new temporary, and returns the value so defined.\nfunc emitLoad(f *Function, addr Value, source ast.Node) *Load {\n\tv := &Load{X: addr}\n\tv.setType(deref(addr.Type()))\n\tf.emit(v, source)\n\treturn v\n}\n\nfunc emitRecv(f *Function, ch Value, commaOk bool, typ types.Type, source ast.Node) Value {\n\trecv := &Recv{\n\t\tChan:    ch,\n\t\tCommaOk: commaOk,\n\t}\n\trecv.setType(typ)\n\treturn f.emit(recv, source)\n}\n\n// emitDebugRef emits to f a DebugRef pseudo-instruction associating\n// expression e with value v.\nfunc emitDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) {\n\tref := makeDebugRef(f, e, v, isAddr)\n\tif ref == nil {\n\t\treturn\n\t}\n\tf.emit(ref, nil)\n}\n\nfunc makeDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) *DebugRef {\n\tif !f.debugInfo() {\n\t\treturn nil // debugging not enabled\n\t}\n\tif v == nil || e == nil {\n\t\tpanic(\"nil\")\n\t}\n\tvar obj types.Object\n\te = unparen(e)\n\tif id, ok := e.(*ast.Ident); ok {\n\t\tif isBlankIdent(id) {\n\t\t\treturn nil\n\t\t}\n\t\tobj = f.Pkg.objectOf(id)\n\t\tswitch obj.(type) {\n\t\tcase *types.Nil, *types.Const, *types.Builtin:\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn &DebugRef{\n\t\tX:      v,\n\t\tExpr:   e,\n\t\tIsAddr: isAddr,\n\t\tobject: obj,\n\t}\n}\n\n// emitArith emits to f code to compute the binary operation op(x, y)\n// where op is an eager shift, logical or arithmetic operation.\n// (Use emitCompare() for comparisons and Builder.logicalBinop() for\n// non-eager operations.)\nfunc emitArith(f *Function, op token.Token, x, y Value, t types.Type, source ast.Node) Value {\n\tswitch op {\n\tcase token.SHL, token.SHR:\n\t\tx = emitConv(f, x, t, source)\n\t\t// y may be signed or an 'untyped' constant.\n\t\t// There is a runtime panic if y is signed and <0. Instead of inserting a check for y<0\n\t\t// and converting to an unsigned value (like the compiler) leave y as is.\n\t\tif b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {\n\t\t\t// Untyped conversion:\n\t\t\t// Spec https://go.dev/ref/spec#Operators:\n\t\t\t// The right operand in a shift expression must have integer type or be an untyped constant\n\t\t\t// representable by a value of type uint.\n\t\t\ty = emitConv(f, y, types.Typ[types.Uint], source)\n\t\t}\n\n\tcase token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:\n\t\tx = emitConv(f, x, t, source)\n\t\ty = emitConv(f, y, t, source)\n\n\tdefault:\n\t\tpanic(\"illegal op in emitArith: \" + op.String())\n\n\t}\n\tv := &BinOp{\n\t\tOp: op,\n\t\tX:  x,\n\t\tY:  y,\n\t}\n\tv.setType(t)\n\treturn f.emit(v, source)\n}\n\n// emitCompare emits to f code compute the boolean result of\n// comparison 'x op y'.\nfunc emitCompare(f *Function, op token.Token, x, y Value, source ast.Node) Value {\n\txt := x.Type().Underlying()\n\tyt := y.Type().Underlying()\n\n\t// Special case to optimise a tagless SwitchStmt so that\n\t// these are equivalent\n\t//   switch { case e: ...}\n\t//   switch true { case e: ... }\n\t//   if e==true { ... }\n\t// even in the case when e's type is an interface.\n\t// TODO(adonovan): opt: generalise to x==true, false!=y, etc.\n\tif x, ok := x.(*Const); ok && op == token.EQL && x.Value != nil && x.Value.Kind() == constant.Bool && constant.BoolVal(x.Value) {\n\t\tif yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 {\n\t\t\treturn y\n\t\t}\n\t}\n\n\tif types.Identical(xt, yt) {\n\t\t// no conversion necessary\n\t} else if _, ok := xt.(*types.Interface); ok && !typeparams.IsTypeParam(x.Type()) {\n\t\ty = emitConv(f, y, x.Type(), source)\n\t} else if _, ok := yt.(*types.Interface); ok && !typeparams.IsTypeParam(y.Type()) {\n\t\tx = emitConv(f, x, y.Type(), source)\n\t} else if _, ok := x.(*Const); ok {\n\t\tx = emitConv(f, x, y.Type(), source)\n\t} else if _, ok := y.(*Const); ok {\n\t\ty = emitConv(f, y, x.Type(), source)\n\t\t//lint:ignore SA9003 no-op\n\t} else {\n\t\t// other cases, e.g. channels.  No-op.\n\t}\n\n\tv := &BinOp{\n\t\tOp: op,\n\t\tX:  x,\n\t\tY:  y,\n\t}\n\tv.setType(tBool)\n\treturn f.emit(v, source)\n}\n\n// isValuePreserving returns true if a conversion from ut_src to\n// ut_dst is value-preserving, i.e. just a change of type.\n// Precondition: neither argument is a named type.\nfunc isValuePreserving(ut_src, ut_dst types.Type) bool {\n\t// Identical underlying types?\n\tif types.IdenticalIgnoreTags(ut_dst, ut_src) {\n\t\treturn true\n\t}\n\n\tswitch ut_dst.(type) {\n\tcase *types.Chan:\n\t\t// Conversion between channel types?\n\t\t_, ok := ut_src.(*types.Chan)\n\t\treturn ok\n\n\tcase *types.Pointer:\n\t\t// Conversion between pointers with identical base types?\n\t\t_, ok := ut_src.(*types.Pointer)\n\t\treturn ok\n\t}\n\treturn false\n}\n\n// emitConv emits to f code to convert Value val to exactly type typ,\n// and returns the converted value.  Implicit conversions are required\n// by language assignability rules in assignments, parameter passing,\n// etc.\nfunc emitConv(f *Function, val Value, t_dst types.Type, source ast.Node) Value {\n\tt_src := val.Type()\n\n\t// Identical types?  Conversion is a no-op.\n\tif types.Identical(t_src, t_dst) {\n\t\treturn val\n\t}\n\n\tut_dst := t_dst.Underlying()\n\tut_src := t_src.Underlying()\n\n\t// Conversion to, or construction of a value of, an interface type?\n\tif isNonTypeParamInterface(t_dst) {\n\t\t// Interface name change?\n\t\tif isValuePreserving(ut_src, ut_dst) {\n\t\t\tc := &ChangeType{X: val}\n\t\t\tc.setType(t_dst)\n\t\t\treturn f.emit(c, source)\n\t\t}\n\n\t\t// Assignment from one interface type to another?\n\t\tif isNonTypeParamInterface(t_src) {\n\t\t\tc := &ChangeInterface{X: val}\n\t\t\tc.setType(t_dst)\n\t\t\treturn f.emit(c, source)\n\t\t}\n\n\t\t// Untyped nil constant?  Return interface-typed nil constant.\n\t\tif ut_src == tUntypedNil {\n\t\t\treturn emitConst(f, zeroConst(t_dst, source))\n\t\t}\n\n\t\t// Convert (non-nil) \"untyped\" literals to their default type.\n\t\tif t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 {\n\t\t\tval = emitConv(f, val, types.Default(ut_src), source)\n\t\t}\n\n\t\tf.Pkg.Prog.needMethodsOf(val.Type())\n\t\tmi := &MakeInterface{X: val}\n\t\tmi.setType(t_dst)\n\t\treturn f.emit(mi, source)\n\t}\n\n\t// In the common case, the typesets of src and dst are singletons\n\t// and we emit an appropriate conversion. But if either contains\n\t// a type parameter, the conversion may represent a cross product,\n\t// in which case which we emit a MultiConvert.\n\ttset_dst := typeutil.NewTypeSet(ut_dst)\n\ttset_src := typeutil.NewTypeSet(ut_src)\n\n\t// conversionCase describes an instruction pattern that may be emitted to\n\t// model d <- s for d in dst_terms and s in src_terms.\n\t// Multiple conversions can match the same pattern.\n\ttype conversionCase uint8\n\tconst (\n\t\tchangeType conversionCase = 1 << iota\n\t\tsliceToArray\n\t\tsliceToArrayPtr\n\t\tsliceTo0Array\n\t\tsliceTo0ArrayPtr\n\t\tconvert\n\t)\n\n\tclassify := func(s, d types.Type) conversionCase {\n\t\t// Just a change of type, but not value or representation?\n\t\tif isValuePreserving(s, d) {\n\t\t\treturn changeType\n\t\t}\n\n\t\t// Conversion from slice to array or slice to array pointer?\n\t\tif slice, ok := s.(*types.Slice); ok {\n\t\t\tvar arr *types.Array\n\t\t\tvar ptr bool\n\t\t\t// Conversion from slice to array pointer?\n\t\t\tswitch d := d.(type) {\n\t\t\tcase *types.Array:\n\t\t\t\tarr = d\n\t\t\tcase *types.Pointer:\n\t\t\t\tarr, _ = d.Elem().Underlying().(*types.Array)\n\t\t\t\tptr = true\n\t\t\t}\n\t\t\tif arr != nil && types.Identical(slice.Elem(), arr.Elem()) {\n\t\t\t\tif arr.Len() == 0 {\n\t\t\t\t\tif ptr {\n\t\t\t\t\t\treturn sliceTo0ArrayPtr\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn sliceTo0Array\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ptr {\n\t\t\t\t\treturn sliceToArrayPtr\n\t\t\t\t} else {\n\t\t\t\t\treturn sliceToArray\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// The only remaining case in well-typed code is a representation-\n\t\t// changing conversion of basic types (possibly with []byte/[]rune).\n\t\tif !isBasic(s) && !isBasic(d) {\n\t\t\tpanic(fmt.Sprintf(\"in %s: cannot convert term %s (%s [within %s]) to type %s [within %s]\", f, val, val.Type(), s, t_dst, d))\n\t\t}\n\t\treturn convert\n\t}\n\n\tvar classifications conversionCase\n\tfor _, s := range tset_src.Terms {\n\t\tus := s.Type().Underlying()\n\t\tfor _, d := range tset_dst.Terms {\n\t\t\tud := d.Type().Underlying()\n\t\t\tclassifications |= classify(us, ud)\n\t\t}\n\t}\n\tif classifications == 0 {\n\t\tpanic(fmt.Sprintf(\"in %s: cannot convert %s (%s) to %s\", f, val, val.Type(), t_dst))\n\t}\n\n\t// Conversion of a compile-time constant value?\n\tif c, ok := val.(*Const); ok {\n\t\t// Conversion to a basic type?\n\t\tif isBasic(ut_dst) {\n\t\t\t// Conversion of a compile-time constant to\n\t\t\t// another constant type results in a new\n\t\t\t// constant of the destination type and\n\t\t\t// (initially) the same abstract value.\n\t\t\t// We don't truncate the value yet.\n\t\t\treturn emitConst(f, NewConst(c.Value, t_dst, source))\n\t\t}\n\t\t// Can we always convert from zero value without panicking?\n\t\tconst mayPanic = sliceToArray | sliceToArrayPtr\n\t\tif c.Value == nil && classifications&mayPanic == 0 {\n\t\t\treturn emitConst(f, NewConst(nil, t_dst, source))\n\t\t}\n\n\t\t// We're converting from constant to non-constant type,\n\t\t// e.g. string -> []byte/[]rune.\n\t}\n\n\tswitch classifications {\n\tcase changeType: // representation-preserving change\n\t\tc := &ChangeType{X: val}\n\t\tc.setType(t_dst)\n\t\treturn f.emit(c, source)\n\n\tcase sliceToArrayPtr, sliceTo0ArrayPtr: // slice to array pointer\n\t\tc := &SliceToArrayPointer{X: val}\n\t\tc.setType(t_dst)\n\t\treturn f.emit(c, source)\n\n\tcase sliceToArray: // slice to arrays (not zero-length)\n\t\tp := &SliceToArray{X: val}\n\t\tp.setType(t_dst)\n\t\treturn f.emit(p, source)\n\n\tcase sliceTo0Array: // slice to zero-length arrays (constant)\n\t\treturn emitConst(f, zeroConst(t_dst, source))\n\n\tcase convert: // representation-changing conversion\n\t\tc := &Convert{X: val}\n\t\tc.setType(t_dst)\n\t\treturn f.emit(c, source)\n\n\tdefault: // multiple conversion\n\t\tc := &MultiConvert{X: val, from: tset_src, to: tset_dst}\n\t\tc.setType(t_dst)\n\t\treturn f.emit(c, source)\n\t}\n}\n\n// emitStore emits to f an instruction to store value val at location\n// addr, applying implicit conversions as required by assignability rules.\nfunc emitStore(f *Function, addr, val Value, source ast.Node) *Store {\n\ts := &Store{\n\t\tAddr: addr,\n\t\tVal:  emitConv(f, val, deref(addr.Type()), source),\n\t}\n\tf.emit(s, source)\n\treturn s\n}\n\n// emitJump emits to f a jump to target, and updates the control-flow graph.\n// Postcondition: f.currentBlock is nil.\nfunc emitJump(f *Function, target *BasicBlock, source ast.Node) *Jump {\n\tb := f.currentBlock\n\tj := new(Jump)\n\tb.emit(j, source)\n\taddEdge(b, target)\n\tf.currentBlock = nil\n\treturn j\n}\n\n// emitIf emits to f a conditional jump to tblock or fblock based on\n// cond, and updates the control-flow graph.\n// Postcondition: f.currentBlock is nil.\nfunc emitIf(f *Function, cond Value, tblock, fblock *BasicBlock, source ast.Node) *If {\n\tb := f.currentBlock\n\tstmt := &If{Cond: cond}\n\tb.emit(stmt, source)\n\taddEdge(b, tblock)\n\taddEdge(b, fblock)\n\tf.currentBlock = nil\n\treturn stmt\n}\n\n// emitExtract emits to f an instruction to extract the index'th\n// component of tuple.  It returns the extracted value.\nfunc emitExtract(f *Function, tuple Value, index int, source ast.Node) Value {\n\te := &Extract{Tuple: tuple, Index: index}\n\te.setType(tuple.Type().(*types.Tuple).At(index).Type())\n\treturn f.emit(e, source)\n}\n\n// emitTypeAssert emits to f a type assertion value := x.(t) and\n// returns the value.  x.Type() must be an interface.\nfunc emitTypeAssert(f *Function, x Value, t types.Type, source ast.Node) Value {\n\ta := &TypeAssert{X: x, AssertedType: t}\n\ta.setType(t)\n\treturn f.emit(a, source)\n}\n\n// emitTypeTest emits to f a type test value,ok := x.(t) and returns\n// a (value, ok) tuple.  x.Type() must be an interface.\nfunc emitTypeTest(f *Function, x Value, t types.Type, source ast.Node) Value {\n\ta := &TypeAssert{\n\t\tX:            x,\n\t\tAssertedType: t,\n\t\tCommaOk:      true,\n\t}\n\ta.setType(types.NewTuple(\n\t\tnewVar(\"value\", t),\n\t\tvarOk,\n\t))\n\treturn f.emit(a, source)\n}\n\n// emitTailCall emits to f a function call in tail position.  The\n// caller is responsible for all fields of 'call' except its type.\n// Intended for wrapper methods.\n// Precondition: f does/will not use deferred procedure calls.\n// Postcondition: f.currentBlock is nil.\nfunc emitTailCall(f *Function, call *Call, source ast.Node) {\n\ttresults := f.Signature.Results()\n\tnr := tresults.Len()\n\tif nr == 1 {\n\t\tcall.typ = tresults.At(0).Type()\n\t} else {\n\t\tcall.typ = tresults\n\t}\n\ttuple := f.emit(call, source)\n\tvar ret Return\n\tswitch nr {\n\tcase 0:\n\t\t// no-op\n\tcase 1:\n\t\tret.Results = []Value{tuple}\n\tdefault:\n\t\tfor i := range nr {\n\t\t\tv := emitExtract(f, tuple, i, source)\n\t\t\t// TODO(adonovan): in principle, this is required:\n\t\t\t//   v = emitConv(f, o.Type, f.Signature.Results[i].Type)\n\t\t\t// but in practice emitTailCall is only used when\n\t\t\t// the types exactly match.\n\t\t\tret.Results = append(ret.Results, v)\n\t\t}\n\t}\n\n\tf.Exit = f.newBasicBlock(\"exit\")\n\temitJump(f, f.Exit, source)\n\tf.currentBlock = f.Exit\n\tf.emit(&ret, source)\n\tf.currentBlock = nil\n}\n\nfunc emitCall(fn *Function, call *Call, source ast.Node) Value {\n\tres := fn.emit(call, source)\n\n\tcallee := call.Call.StaticCallee()\n\tif callee != nil &&\n\t\tcallee.object != nil &&\n\t\tfn.Prog.noReturn != nil &&\n\t\tfn.Prog.noReturn(callee.object) {\n\t\t// Call doesn't return normally. Either it doesn't return at all\n\t\t// (infinitely blocked or exitting the process), or it unwinds the stack\n\t\t// (panic, runtime.Goexit). In case it unwinds, jump to the exit block.\n\t\tfn.emit(new(Jump), source)\n\t\taddEdge(fn.currentBlock, fn.Exit)\n\t\tfn.currentBlock = fn.newBasicBlock(\"unreachable\")\n\t}\n\n\treturn res\n}\n\n// emitImplicitSelections emits to f code to apply the sequence of\n// implicit field selections specified by indices to base value v, and\n// returns the selected value.\n//\n// If v is the address of a struct, the result will be the address of\n// a field; if it is the value of a struct, the result will be the\n// value of a field.\nfunc emitImplicitSelections(f *Function, v Value, indices []int, source ast.Node) Value {\n\tfor _, index := range indices {\n\t\t// We may have a generic type containing a pointer, or a pointer to a generic type containing a struct. A\n\t\t// pointer to a generic containing a pointer to a struct shouldn't be possible because the outer pointer gets\n\t\t// dereferenced implicitly before we get here.\n\t\tfld := typeutil.CoreType(deref(v.Type())).Underlying().(*types.Struct).Field(index)\n\n\t\tif isPointer(v.Type()) {\n\t\t\tinstr := &FieldAddr{\n\t\t\t\tX:     v,\n\t\t\t\tField: index,\n\t\t\t}\n\t\t\tinstr.setType(types.NewPointer(fld.Type()))\n\t\t\tv = f.emit(instr, source)\n\t\t\t// Load the field's value iff indirectly embedded.\n\t\t\tif isPointer(fld.Type()) {\n\t\t\t\tv = emitLoad(f, v, source)\n\t\t\t}\n\t\t} else {\n\t\t\tinstr := &Field{\n\t\t\t\tX:     v,\n\t\t\t\tField: index,\n\t\t\t}\n\t\t\tinstr.setType(fld.Type())\n\t\t\tv = f.emit(instr, source)\n\t\t}\n\t}\n\treturn v\n}\n\n// emitFieldSelection emits to f code to select the index'th field of v.\n//\n// If wantAddr, the input must be a pointer-to-struct and the result\n// will be the field's address; otherwise the result will be the\n// field's value.\n// Ident id is used for position and debug info.\nfunc emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {\n\t// We may have a generic type containing a pointer, or a pointer to a generic type containing a struct. A\n\t// pointer to a generic containing a pointer to a struct shouldn't be possible because the outer pointer gets\n\t// dereferenced implicitly before we get here.\n\tvut := typeutil.CoreType(deref(v.Type())).Underlying().(*types.Struct)\n\tfld := vut.Field(index)\n\tif isPointer(v.Type()) {\n\t\tinstr := &FieldAddr{\n\t\t\tX:     v,\n\t\t\tField: index,\n\t\t}\n\t\tinstr.setSource(id)\n\t\tinstr.setType(types.NewPointer(fld.Type()))\n\t\tv = f.emit(instr, id)\n\t\t// Load the field's value iff we don't want its address.\n\t\tif !wantAddr {\n\t\t\tv = emitLoad(f, v, id)\n\t\t}\n\t} else {\n\t\tinstr := &Field{\n\t\t\tX:     v,\n\t\t\tField: index,\n\t\t}\n\t\tinstr.setSource(id)\n\t\tinstr.setType(fld.Type())\n\t\tv = f.emit(instr, id)\n\t}\n\temitDebugRef(f, id, v, wantAddr)\n\treturn v\n}\n\n// zeroValue emits to f code to produce a zero value of type t,\n// and returns it.\nfunc zeroValue(f *Function, t types.Type, source ast.Node) Value {\n\treturn emitConst(f, zeroConst(t, source))\n}\n\ntype constKey struct {\n\ttyp    types.Type\n\tvalue  constant.Value\n\tsource ast.Node\n}\n\nfunc emitConst(f *Function, c Constant) Constant {\n\tif f.consts == nil {\n\t\tf.consts = map[constKey]constValue{}\n\t}\n\n\ttyp := c.Type()\n\tvar val constant.Value\n\tswitch c := c.(type) {\n\tcase *Const:\n\t\tval = c.Value\n\tcase *ArrayConst, *GenericConst:\n\t\t// These can only represent zero values, so all we need is the type\n\tcase *AggregateConst:\n\t\tcandidates, _ := f.aggregateConsts.At(c.typ)\n\t\tfor _, candidate := range candidates {\n\t\t\tif c.equal(candidate) {\n\t\t\t\treturn candidate\n\t\t\t}\n\t\t}\n\n\t\tfor i := range c.Values {\n\t\t\tc.Values[i] = emitConst(f, c.Values[i].(Constant))\n\t\t}\n\n\t\tc.setBlock(f.Blocks[0])\n\t\trands := c.Operands(nil)\n\t\tupdateOperandsReferrers(c, rands)\n\t\tcandidates = append(candidates, c)\n\t\tf.aggregateConsts.Set(c.typ, candidates)\n\t\treturn c\n\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unexpected type %T\", c))\n\t}\n\tk := constKey{\n\t\ttyp:    typ,\n\t\tvalue:  val,\n\t\tsource: c.Source(),\n\t}\n\tdup, ok := f.consts[k]\n\tif ok {\n\t\treturn dup.c\n\t} else {\n\t\tc.setBlock(f.Blocks[0])\n\t\tf.consts[k] = constValue{\n\t\t\tc:   c,\n\t\t\tidx: len(f.consts),\n\t\t}\n\t\trands := c.Operands(nil)\n\t\tupdateOperandsReferrers(c, rands)\n\t\treturn c\n\t}\n}\n"
  },
  {
    "path": "go/ir/example_test.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir_test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/importer\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\n\t\"golang.org/x/tools/go/packages\"\n)\n\nconst hello = `\npackage main\n\nimport \"fmt\"\n\nconst message = \"Hello, World!\"\n\nfunc main() {\n\tfmt.Println(message)\n}\n`\n\n// This program demonstrates how to run the IR builder on a single\n// package of one or more already-parsed files.  Its dependencies are\n// loaded from compiler export data.  This is what you'd typically use\n// for a compiler; it does not depend on golang.org/x/tools/go/loader.\n//\n// It shows the printed representation of packages, functions, and\n// instructions.  Within the function listing, the name of each\n// BasicBlock such as \".0.entry\" is printed left-aligned, followed by\n// the block's Instructions.\n//\n// For each instruction that defines an IR virtual register\n// (i.e. implements Value), the type of that value is shown in the\n// right column.\n//\n// Build and run the irdump.go program if you want a standalone tool\n// with similar functionality. It is located at\n// honnef.co/go/tools/internal/cmd/irdump.\nfunc Example_buildPackage() {\n\t// Parse the source files.\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"hello.go\", hello, parser.ParseComments|parser.SkipObjectResolution)\n\tif err != nil {\n\t\tfmt.Print(err) // parse error\n\t\treturn\n\t}\n\tfiles := []*ast.File{f}\n\n\t// Create the type-checker's package.\n\tpkg := types.NewPackage(\"hello\", \"\")\n\n\t// Type-check the package, load dependencies.\n\t// Create and build the IR program.\n\thello, _, err := irutil.BuildPackage(\n\t\t&types.Config{Importer: importer.Default()}, fset, pkg, files, ir.SanityCheckFunctions)\n\tif err != nil {\n\t\tfmt.Print(err) // type error in some package\n\t\treturn\n\t}\n\n\t// Print out the package.\n\thello.WriteTo(os.Stdout)\n\n\t// Print out the package-level functions.\n\t// Replace interface{} with any so the tests work for Go 1.17 and Go 1.18.\n\t{\n\t\tvar buf bytes.Buffer\n\t\tir.WriteFunction(&buf, hello.Func(\"init\"))\n\t\tfmt.Print(strings.ReplaceAll(buf.String(), \"interface{}\", \"any\"))\n\t}\n\t{\n\t\tvar buf bytes.Buffer\n\t\tir.WriteFunction(&buf, hello.Func(\"main\"))\n\t\tfmt.Print(strings.ReplaceAll(buf.String(), \"interface{}\", \"any\"))\n\t}\n\n\t// Output:\n\t// package hello:\n\t//   func  init       func()\n\t//   var   init$guard bool\n\t//   func  main       func()\n\t//   const message    message = Const <untyped string> {\"Hello, World!\"}\n\t//\n\t// # Name: hello.init\n\t// # Package: hello\n\t// # Synthetic: package initializer\n\t// func init():\n\t// b0: # entry\n\t// \tt1 = Const <bool> {true}\n\t// \tt2 = Load <bool> init$guard\n\t// \tIf t2 → b1 b2\n\t//\n\t// b1: ← b0 b2 # exit\n\t// \tReturn\n\t//\n\t// b2: ← b0 # init.start\n\t// \tStore {bool} init$guard t1\n\t// \tt6 = Call <()> fmt.init\n\t// \tJump → b1\n\t//\n\t// # Name: hello.main\n\t// # Package: hello\n\t// # Location: hello.go:8:1\n\t// func main():\n\t// b0: # entry\n\t// \tt1 = Const <string> {\"Hello, World!\"}\n\t// \tt2 = Const <int> {0}\n\t// \tt3 = HeapAlloc <*[1]any> # varargs\n\t// \tt4 = IndexAddr <*any> t3 t2\n\t// \tt5 = MakeInterface <any> t1\n\t// \tStore {any} t4 t5\n\t// \tt7 = Slice <[]any> t3 <nil> <nil> <nil>\n\t// \tt8 = Call <(n int, err error)> fmt.Println t7\n\t// \tJump → b1\n\t//\n\t// b1: ← b0 # exit\n\t// \tReturn\n}\n\n// This example builds IR code for a set of packages using the\n// x/tools/go/packages API. This is what you would typically use for a\n// analysis capable of operating on a single package.\nfunc Example_loadPackages() {\n\t// Load, parse, and type-check the initial packages.\n\tcfg := &packages.Config{Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo}\n\tinitial, err := packages.Load(cfg, \"fmt\", \"net/http\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Stop if any package had errors.\n\t// This step is optional; without it, the next step\n\t// will create IR for only a subset of packages.\n\tif packages.PrintErrors(initial) > 0 {\n\t\tlog.Fatalf(\"packages contain errors\")\n\t}\n\n\t// Create IR packages for all well-typed packages.\n\tprog, pkgs := irutil.Packages(initial, ir.PrintPackages, nil)\n\t_ = prog\n\n\t// Build IR code for the well-typed initial packages.\n\tfor _, p := range pkgs {\n\t\tif p != nil {\n\t\t\tp.Build()\n\t\t}\n\t}\n}\n\n// This example builds IR code for a set of packages plus all their dependencies,\n// using the x/tools/go/packages API.\n// This is what you'd typically use for a whole-program analysis.\nfunc Example_loadWholeProgram() {\n\t// Load, parse, and type-check the whole program.\n\tcfg := packages.Config{Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps}\n\tinitial, err := packages.Load(&cfg, \"fmt\", \"net/http\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Create IR packages for well-typed packages and their dependencies.\n\tprog, pkgs := irutil.AllPackages(initial, ir.PrintPackages, nil)\n\t_ = pkgs\n\n\t// Build IR code for the whole program.\n\tprog.Build()\n}\n"
  },
  {
    "path": "go/ir/func.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file implements the Function and BasicBlock types.\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/format\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/go/types/typeutil\"\n)\n\n// addEdge adds a control-flow graph edge from from to to.\nfunc addEdge(from, to *BasicBlock) {\n\tfrom.Succs = append(from.Succs, to)\n\tto.Preds = append(to.Preds, from)\n}\n\n// Control returns the last instruction in the block.\nfunc (b *BasicBlock) Control() Instruction {\n\tif len(b.Instrs) == 0 {\n\t\treturn nil\n\t}\n\treturn b.Instrs[len(b.Instrs)-1]\n}\n\n// SigmaFor returns the sigma node for v coming from pred.\nfunc (b *BasicBlock) SigmaFor(v Value, pred *BasicBlock) *Sigma {\n\tfor _, instr := range b.Instrs {\n\t\tsigma, ok := instr.(*Sigma)\n\t\tif !ok {\n\t\t\t// no more sigmas\n\t\t\treturn nil\n\t\t}\n\t\tif sigma.From == pred && sigma.X == v {\n\t\t\treturn sigma\n\t\t}\n\t}\n\treturn nil\n}\n\n// Parent returns the function that contains block b.\nfunc (b *BasicBlock) Parent() *Function { return b.parent }\n\n// String returns a human-readable label of this block.\n// It is not guaranteed unique within the function.\nfunc (b *BasicBlock) String() string {\n\treturn fmt.Sprintf(\"%d\", b.Index)\n}\n\n// emit appends an instruction to the current basic block.\n// If the instruction defines a Value, it is returned.\nfunc (b *BasicBlock) emit(i Instruction, source ast.Node) Value {\n\ti.setSource(source)\n\ti.setBlock(b)\n\tb.Instrs = append(b.Instrs, i)\n\tv, _ := i.(Value)\n\treturn v\n}\n\n// predIndex returns the i such that b.Preds[i] == c or panics if\n// there is none.\nfunc (b *BasicBlock) predIndex(c *BasicBlock) int {\n\tfor i, pred := range b.Preds {\n\t\tif pred == c {\n\t\t\treturn i\n\t\t}\n\t}\n\tpanic(fmt.Sprintf(\"no edge %s -> %s\", c, b))\n}\n\n// succIndex returns the i such that b.Succs[i] == c or -1 if there is none.\nfunc (b *BasicBlock) succIndex(c *BasicBlock) int {\n\tfor i, succ := range b.Succs {\n\t\tif succ == c {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\n// hasPhi returns true if b.Instrs contains φ-nodes.\nfunc (b *BasicBlock) hasPhi() bool {\n\t_, ok := b.Instrs[0].(*Phi)\n\treturn ok\n}\n\nfunc (b *BasicBlock) Phis() []Instruction {\n\treturn b.phis()\n}\n\n// phis returns the prefix of b.Instrs containing all the block's φ-nodes.\nfunc (b *BasicBlock) phis() []Instruction {\n\tfor i, instr := range b.Instrs {\n\t\tif _, ok := instr.(*Phi); !ok {\n\t\t\treturn b.Instrs[:i]\n\t\t}\n\t}\n\treturn nil // unreachable in well-formed blocks\n}\n\n// replacePred replaces all occurrences of p in b's predecessor list with q.\n// Ordinarily there should be at most one.\nfunc (b *BasicBlock) replacePred(p, q *BasicBlock) {\n\tfor i, pred := range b.Preds {\n\t\tif pred == p {\n\t\t\tb.Preds[i] = q\n\t\t}\n\t}\n}\n\n// replaceSucc replaces all occurrences of p in b's successor list with q.\n// Ordinarily there should be at most one.\nfunc (b *BasicBlock) replaceSucc(p, q *BasicBlock) {\n\tfor i, succ := range b.Succs {\n\t\tif succ == p {\n\t\t\tb.Succs[i] = q\n\t\t}\n\t}\n}\n\n// removePred removes all occurrences of p in b's\n// predecessor list and φ-nodes.\n// Ordinarily there should be at most one.\nfunc (b *BasicBlock) removePred(p *BasicBlock) {\n\tphis := b.phis()\n\n\t// We must preserve edge order for φ-nodes.\n\tj := 0\n\tfor i, pred := range b.Preds {\n\t\tif pred != p {\n\t\t\tb.Preds[j] = b.Preds[i]\n\t\t\t// Strike out φ-edge too.\n\t\t\tfor _, instr := range phis {\n\t\t\t\tphi := instr.(*Phi)\n\t\t\t\tphi.Edges[j] = phi.Edges[i]\n\t\t\t}\n\t\t\tj++\n\t\t}\n\t}\n\t// Nil out b.Preds[j:] and φ-edges[j:] to aid GC.\n\tfor i := j; i < len(b.Preds); i++ {\n\t\tb.Preds[i] = nil\n\t\tfor _, instr := range phis {\n\t\t\tinstr.(*Phi).Edges[i] = nil\n\t\t}\n\t}\n\tb.Preds = b.Preds[:j]\n\tfor _, instr := range phis {\n\t\tphi := instr.(*Phi)\n\t\tphi.Edges = phi.Edges[:j]\n\t}\n}\n\n// Destinations associated with unlabelled for/switch/select stmts.\n// We push/pop one of these as we enter/leave each construct and for\n// each BranchStmt we scan for the innermost target of the right type.\ntype targets struct {\n\ttail         *targets // rest of stack\n\t_break       *BasicBlock\n\t_continue    *BasicBlock\n\t_fallthrough *BasicBlock\n}\n\n// Destinations associated with a labelled block.\n// We populate these as labels are encountered in forward gotos or\n// labelled statements.\n// Forward gotos are resolved once it is known which statement they\n// are associated with inside the Function.\ntype lblock struct {\n\tlabel     *types.Label // Label targeted by the blocks.\n\tresolved  bool         // _goto block encountered (back jump or resolved fwd jump)\n\t_goto     *BasicBlock\n\t_break    *BasicBlock\n\t_continue *BasicBlock\n}\n\n// label returns the symbol denoted by a label identifier.\n//\n// label should be a non-blank identifier (label.Name != \"_\").\nfunc (f *Function) label(label *ast.Ident) *types.Label {\n\treturn f.Pkg.objectOf(label).(*types.Label)\n}\n\n// lblockOf returns the branch target associated with the\n// specified label, creating it if needed.\nfunc (f *Function) lblockOf(label *types.Label) *lblock {\n\tlb := f.lblocks[label]\n\tif lb == nil {\n\t\tlb = &lblock{\n\t\t\tlabel: label,\n\t\t\t_goto: f.newBasicBlock(label.Name()),\n\t\t}\n\t\tif f.lblocks == nil {\n\t\t\tf.lblocks = make(map[*types.Label]*lblock)\n\t\t}\n\t\tf.lblocks[label] = lb\n\t}\n\treturn lb\n}\n\n// labelledBlock searches f for the block of the specified label.\n//\n// If f is a yield function, it additionally searches ancestor Functions\n// corresponding to enclosing range-over-func statements within the\n// same source function, so the returned block may belong to a different Function.\nfunc labelledBlock(f *Function, label *types.Label, tok token.Token) *BasicBlock {\n\tif lb := f.lblocks[label]; lb != nil {\n\t\tvar block *BasicBlock\n\t\tswitch tok {\n\t\tcase token.BREAK:\n\t\t\tblock = lb._break\n\t\tcase token.CONTINUE:\n\t\t\tblock = lb._continue\n\t\tcase token.GOTO:\n\t\t\tblock = lb._goto\n\t\t}\n\t\tif block != nil {\n\t\t\treturn block\n\t\t}\n\t}\n\t// Search ancestors if this is a yield function.\n\tif f.jump != nil {\n\t\treturn labelledBlock(f.parent, label, tok)\n\t}\n\treturn nil\n}\n\n// targetedBlock looks for the nearest block in f.targets\n// (and f's ancestors) that matches tok's type, and returns\n// the block and function it was found in.\nfunc targetedBlock(f *Function, tok token.Token) *BasicBlock {\n\tif f == nil {\n\t\treturn nil\n\t}\n\tfor t := f.targets; t != nil; t = t.tail {\n\t\tvar block *BasicBlock\n\t\tswitch tok {\n\t\tcase token.BREAK:\n\t\t\tblock = t._break\n\t\tcase token.CONTINUE:\n\t\t\tblock = t._continue\n\t\tcase token.FALLTHROUGH:\n\t\t\tblock = t._fallthrough\n\t\t}\n\t\tif block != nil {\n\t\t\treturn block\n\t\t}\n\t}\n\t// Search f's ancestors (in case f is a yield function).\n\treturn targetedBlock(f.parent, tok)\n}\n\n// addResultVar adds a result for a variable v to f.results and v to f.returnVars.\nfunc (f *Function) addResultVar(v *types.Var, source ast.Node) {\n\tname := v.Name()\n\tif name == \"\" {\n\t\tname = fmt.Sprintf(\"res.%d\", len(f.results))\n\t}\n\tresult := emitLocalVar(f, v, source)\n\tresult.comment = name\n\tf.results = append(f.results, result)\n\tf.returnVars = append(f.returnVars, v)\n}\n\nfunc (f *Function) addParamVar(v *types.Var, source ast.Node) *Parameter {\n\tname := v.Name()\n\tif name == \"\" {\n\t\tname = fmt.Sprintf(\"arg%d\", len(f.Params))\n\t}\n\tvar b *BasicBlock\n\tif len(f.Blocks) > 0 {\n\t\tb = f.Blocks[0]\n\t}\n\tparam := &Parameter{name: name}\n\tparam.setBlock(b)\n\tparam.setType(v.Type())\n\tparam.setSource(source)\n\tparam.object = v\n\tf.Params = append(f.Params, param)\n\tif b != nil {\n\t\tf.Blocks[0].Instrs = append(f.Blocks[0].Instrs, param)\n\t}\n\treturn param\n}\n\n// addSpilledParam declares a parameter that is pre-spilled to the\n// stack; the function body will load/store the spilled location.\n// Subsequent lifting will eliminate spills where possible.\nfunc (f *Function) addSpilledParam(obj *types.Var, source ast.Node) {\n\tparam := f.addParamVar(obj, source)\n\tspill := emitLocalVar(f, obj, source)\n\temitStore(f, spill, param, source)\n\t// f.emit(&Store{Addr: spill, Val: param})\n}\n\n// startBody initializes the function prior to generating IR code for its body.\n// Precondition: f.Type() already set.\nfunc (f *Function) startBody() {\n\tentry := f.newBasicBlock(\"entry\")\n\tf.currentBlock = entry\n\tf.vars = make(map[*types.Var]Value) // needed for some synthetics, e.g. init\n}\n\nfunc (f *Function) blockset(i int) *BlockSet {\n\tbs := &f.blocksets[i]\n\tif len(bs.values) != len(f.Blocks) {\n\t\tif cap(bs.values) >= len(f.Blocks) {\n\t\t\tbs.values = bs.values[:len(f.Blocks)]\n\t\t\tbs.Clear()\n\t\t} else {\n\t\t\tbs.values = make([]bool, len(f.Blocks))\n\t\t}\n\t} else {\n\t\tbs.Clear()\n\t}\n\treturn bs\n}\n\nfunc (f *Function) exitBlock() {\n\told := f.currentBlock\n\n\tf.Exit = f.newBasicBlock(\"exit\")\n\tf.currentBlock = f.Exit\n\n\tresults := make([]Value, len(f.results))\n\t// Run function calls deferred in this\n\t// function when explicitly returning from it.\n\tf.emit(new(RunDefers), nil)\n\tfor i, r := range f.results {\n\t\tresults[i] = emitLoad(f, r, nil)\n\t}\n\n\tf.emit(&Return{Results: results}, nil)\n\tf.currentBlock = old\n}\n\n// createSyntacticParams populates f.Params and generates code (spills\n// and named result locals) for all the parameters declared in the\n// syntax.  In addition it populates the f.objects mapping.\n//\n// Preconditions:\n// f.startBody() was called.\n// Postcondition:\n// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)\nfunc (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {\n\t// Receiver (at most one inner iteration).\n\tif recv != nil {\n\t\tfor _, field := range recv.List {\n\t\t\tfor _, n := range field.Names {\n\t\t\t\tf.addSpilledParam(identVar(f, n), n)\n\t\t\t}\n\t\t\t// Anonymous receiver?  No need to spill.\n\t\t\tif field.Names == nil {\n\t\t\t\tf.addParamVar(f.Signature.Recv(), field)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Parameters.\n\tif functype.Params != nil {\n\t\tn := len(f.Params) // 1 if has recv, 0 otherwise\n\t\tfor _, field := range functype.Params.List {\n\t\t\tfor _, n := range field.Names {\n\t\t\t\tf.addSpilledParam(identVar(f, n), n)\n\t\t\t}\n\t\t\t// Anonymous parameter?  No need to spill.\n\t\t\tif field.Names == nil {\n\t\t\t\tf.addParamVar(f.Signature.Params().At(len(f.Params)-n), field)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Results.\n\tif functype.Results != nil {\n\t\tfor _, field := range functype.Results.List {\n\t\t\t// Implicit \"var\" decl of locals for named results.\n\t\t\tfor _, n := range field.Names {\n\t\t\t\tv := identVar(f, n)\n\t\t\t\tf.addResultVar(v, n)\n\t\t\t}\n\t\t\t// Implicit \"var\" decl of local for an unnamed result.\n\t\t\tif field.Names == nil {\n\t\t\t\tv := f.Signature.Results().At(len(f.results))\n\t\t\t\tf.addResultVar(v, field.Type)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// createDeferStack initializes fn.deferstack to a local variable\n// initialized to a ssa:deferstack() call.\nfunc (fn *Function) createDeferStack() {\n\t// Each syntactic function makes a call to ssa:deferstack,\n\t// which is spilled to a local. Unused ones are later removed.\n\tfn.deferstack = newVar(\"defer$stack\", tDeferStack)\n\tcall := &Call{Call: CallCommon{Value: vDeferStack}}\n\tcall.setType(tDeferStack)\n\tdeferstack := fn.emit(call, nil)\n\tspill := emitLocalVar(fn, fn.deferstack, nil)\n\temitStore(fn, spill, deferstack, nil)\n}\n\nfunc numberNodes(f *Function) {\n\tvar base ID\n\tfor _, b := range f.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tif instr == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tbase++\n\t\t\tinstr.setID(base)\n\t\t}\n\t}\n}\n\nfunc updateOperandsReferrers(instr Instruction, ops []*Value) {\n\tfor _, op := range ops {\n\t\tif r := *op; r != nil {\n\t\t\tif refs := (*op).Referrers(); refs != nil {\n\t\t\t\tif len(*refs) == 0 {\n\t\t\t\t\t// per median, each value has two referrers, so we can avoid one call into growslice\n\t\t\t\t\t//\n\t\t\t\t\t// Note: we experimented with allocating\n\t\t\t\t\t// sequential scratch space, but we\n\t\t\t\t\t// couldn't find a value that gave better\n\t\t\t\t\t// performance than making many individual\n\t\t\t\t\t// allocations\n\t\t\t\t\t*refs = make([]Instruction, 1, 2)\n\t\t\t\t\t(*refs)[0] = instr\n\t\t\t\t} else {\n\t\t\t\t\t*refs = append(*refs, instr)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// buildReferrers populates the def/use information in all non-nil\n// Value.Referrers slice.\n// Precondition: all such slices are initially empty.\nfunc buildReferrers(f *Function) {\n\tvar rands []*Value\n\n\tfor _, b := range f.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\trands = instr.Operands(rands[:0]) // recycle storage\n\t\t\tupdateOperandsReferrers(instr, rands)\n\t\t}\n\t}\n\n\tfor _, c := range f.consts {\n\t\trands = c.c.Operands(rands[:0])\n\t\tupdateOperandsReferrers(c.c, rands)\n\t}\n}\n\nfunc (f *Function) emitConsts() {\n\tdefer func() {\n\t\tf.consts = nil\n\t\tf.aggregateConsts = typeutil.Map[[]*AggregateConst]{}\n\t}()\n\n\tif len(f.Blocks) == 0 {\n\t\treturn\n\t}\n\n\t// TODO(dh): our deduplication only works on booleans and\n\t// integers. other constants are represented as pointers to\n\t// things.\n\thead := make([]constValue, 0, len(f.consts))\n\tfor _, c := range f.consts {\n\t\tif len(*c.c.Referrers()) == 0 {\n\t\t\t// TODO(dh): killing a const may make other consts dead, too\n\t\t\tkillInstruction(c.c)\n\t\t} else {\n\t\t\thead = append(head, c)\n\t\t}\n\t}\n\tsort.Slice(head, func(i, j int) bool {\n\t\treturn head[i].idx < head[j].idx\n\t})\n\tentry := f.Blocks[0]\n\tinstrs := make([]Instruction, 0, len(entry.Instrs)+len(head))\n\tfor _, c := range head {\n\t\tinstrs = append(instrs, c.c)\n\t}\n\tf.aggregateConsts.Iterate(func(key types.Type, value []*AggregateConst) {\n\t\tfor _, c := range value {\n\t\t\tinstrs = append(instrs, c)\n\t\t}\n\t})\n\n\tinstrs = append(instrs, entry.Instrs...)\n\tentry.Instrs = instrs\n}\n\n// buildFakeExits ensures that every block in the function is\n// reachable in reverse from the Exit block. This is required to build\n// a full post-dominator tree, and to ensure the exit block's\n// inclusion in the dominator tree.\nfunc buildFakeExits(fn *Function) {\n\t// Find back-edges via forward DFS\n\tfn.fakeExits = BlockSet{values: make([]bool, len(fn.Blocks))}\n\tseen := fn.blockset(0)\n\tbackEdges := fn.blockset(1)\n\n\tvar dfs func(b *BasicBlock)\n\tdfs = func(b *BasicBlock) {\n\t\tif !seen.Add(b) {\n\t\t\tbackEdges.Add(b)\n\t\t\treturn\n\t\t}\n\t\tfor _, pred := range b.Succs {\n\t\t\tdfs(pred)\n\t\t}\n\t}\n\tdfs(fn.Blocks[0])\nbuildLoop:\n\tfor {\n\t\tseen := fn.blockset(2)\n\t\tvar dfs func(b *BasicBlock)\n\t\tdfs = func(b *BasicBlock) {\n\t\t\tif !seen.Add(b) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor _, pred := range b.Preds {\n\t\t\t\tdfs(pred)\n\t\t\t}\n\t\t\tif b == fn.Exit {\n\t\t\t\tfor _, b := range fn.Blocks {\n\t\t\t\t\tif fn.fakeExits.Has(b) {\n\t\t\t\t\t\tdfs(b)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdfs(fn.Exit)\n\n\t\tfor _, b := range fn.Blocks {\n\t\t\tif !seen.Has(b) && backEdges.Has(b) {\n\t\t\t\t// Block b is not reachable from the exit block. Add a\n\t\t\t\t// fake jump from b to exit, then try again. Note that we\n\t\t\t\t// only add one fake edge at a time, as it may make\n\t\t\t\t// multiple blocks reachable.\n\t\t\t\t//\n\t\t\t\t// We only consider those blocks that have back edges.\n\t\t\t\t// Any unreachable block that doesn't have a back edge\n\t\t\t\t// must flow into a loop, which by definition has a\n\t\t\t\t// back edge. Thus, by looking for loops, we should\n\t\t\t\t// need fewer fake edges overall.\n\t\t\t\tfn.fakeExits.Add(b)\n\t\t\t\tcontinue buildLoop\n\t\t\t}\n\t\t}\n\n\t\tbreak\n\t}\n}\n\n// finishBody() finalizes the function after IR code generation of its body.\nfunc (f *Function) finishBody() {\n\tf.currentBlock = nil\n\tf.lblocks = nil\n\n\t// Remove from f.Locals any Allocs that escape to the heap.\n\tj := 0\n\tfor _, l := range f.Locals {\n\t\tif !l.Heap {\n\t\t\tf.Locals[j] = l\n\t\t\tj++\n\t\t}\n\t}\n\t// Nil out f.Locals[j:] to aid GC.\n\tfor i := j; i < len(f.Locals); i++ {\n\t\tf.Locals[i] = nil\n\t}\n\tf.Locals = f.Locals[:j]\n\n\toptimizeBlocks(f)\n\tbuildFakeExits(f)\n\tbuildReferrers(f)\n\tbuildDomTree(f)\n\tbuildPostDomTree(f)\n\n\tif f.Prog.mode&NaiveForm == 0 {\n\t\tfor lift(f) {\n\t\t}\n\t\tif doSimplifyConstantCompositeValues {\n\t\t\tfor simplifyConstantCompositeValues(f) {\n\t\t\t}\n\t\t}\n\t}\n\n\t// emit constants after lifting, because lifting may produce new constants, but before other variable splitting,\n\t// because it expects constants to have been deduplicated.\n\tf.emitConsts()\n\n\tif f.Prog.mode&SplitAfterNewInformation != 0 {\n\t\tsplitOnNewInformation(f.Blocks[0], &StackMap{})\n\t}\n\n\t// clear remaining builder state\n\tf.results = nil    // (used by lifting)\n\tf.deferstack = nil // (used by lifting)\n\tf.vars = nil       // (used by lifting)\n\tf.goversion = \"\"\n\n\tnumberNodes(f)\n\n\tdefer f.wr.Close()\n\tf.wr.WriteFunc(\"start\", \"start\", f)\n\n\tif f.Prog.mode&PrintFunctions != 0 {\n\t\tprintMu.Lock()\n\t\tf.WriteTo(os.Stdout)\n\t\tprintMu.Unlock()\n\t}\n\n\tif f.Prog.mode&SanityCheckFunctions != 0 {\n\t\tmustSanityCheck(f, nil)\n\t}\n}\n\nfunc isUselessPhi(phi *Phi) (Value, bool) {\n\tvar v0 Value\n\tfor _, e := range phi.Edges {\n\t\tif e == phi {\n\t\t\tcontinue\n\t\t}\n\t\tif v0 == nil {\n\t\t\tv0 = e\n\t\t}\n\t\tif v0 != e {\n\t\t\tif v0, ok := v0.(*Const); ok {\n\t\t\t\tif e, ok := e.(*Const); ok {\n\t\t\t\t\tif v0.typ == e.typ && v0.Value == e.Value {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil, false\n\t\t}\n\t}\n\treturn v0, true\n}\n\nfunc (f *Function) RemoveNilBlocks() {\n\tf.removeNilBlocks()\n}\n\n// removeNilBlocks eliminates nils from f.Blocks and updates each\n// BasicBlock.Index.  Use this after any pass that may delete blocks.\nfunc (f *Function) removeNilBlocks() {\n\tj := 0\n\tfor _, b := range f.Blocks {\n\t\tif b != nil {\n\t\t\tb.Index = j\n\t\t\tf.Blocks[j] = b\n\t\t\tj++\n\t\t}\n\t}\n\t// Nil out f.Blocks[j:] to aid GC.\n\tfor i := j; i < len(f.Blocks); i++ {\n\t\tf.Blocks[i] = nil\n\t}\n\tf.Blocks = f.Blocks[:j]\n}\n\n// SetDebugMode sets the debug mode for package pkg.  If true, all its\n// functions will include full debug info.  This greatly increases the\n// size of the instruction stream, and causes Functions to depend upon\n// the ASTs, potentially keeping them live in memory for longer.\nfunc (pkg *Package) SetDebugMode(debug bool) {\n\t// TODO(adonovan): do we want ast.File granularity?\n\tpkg.debug = debug\n}\n\n// debugInfo reports whether debug info is wanted for this function.\nfunc (f *Function) debugInfo() bool {\n\treturn f.Pkg != nil && f.Pkg.debug\n}\n\n// lookup returns the address of the named variable identified by obj\n// that is local to function f or one of its enclosing functions.\n// If escaping, the reference comes from a potentially escaping pointer\n// expression and the referent must be heap-allocated.\n// We assume the referent is a *Alloc or *Phi.\n// (The only Phis at this stage are those created directly by go1.22 \"for\" loops.)\nfunc (f *Function) lookup(obj *types.Var, escaping bool) Value {\n\tif v, ok := f.vars[obj]; ok {\n\t\tif escaping {\n\t\t\tswitch v := v.(type) {\n\t\t\tcase *Alloc:\n\t\t\t\tv.Heap = true\n\t\t\tcase *Phi:\n\t\t\t\tfor _, edge := range v.Edges {\n\t\t\t\t\tif alloc, ok := edge.(*Alloc); ok {\n\t\t\t\t\t\talloc.Heap = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn v // function-local var (address)\n\t}\n\n\t// Definition must be in an enclosing function;\n\t// plumb it through intervening closures.\n\tif f.parent == nil {\n\t\tpanic(\"no ir.Value for \" + obj.String())\n\t}\n\touter := f.parent.lookup(obj, true) // escaping\n\tv := &FreeVar{\n\t\tname:   obj.Name(),\n\t\ttyp:    outer.Type(),\n\t\touter:  outer,\n\t\tparent: f,\n\t}\n\tf.vars[obj] = v\n\tf.FreeVars = append(f.FreeVars, v)\n\treturn v\n}\n\n// emit emits the specified instruction to function f.\nfunc (f *Function) emit(instr Instruction, source ast.Node) Value {\n\treturn f.currentBlock.emit(instr, source)\n}\n\n// RelString returns the full name of this function, qualified by\n// package name, receiver type, etc.\n//\n// The specific formatting rules are not guaranteed and may change.\n//\n// Examples:\n//\n//\t\"math.IsNaN\"                  // a package-level function\n//\t\"(*bytes.Buffer).Bytes\"       // a declared method or a wrapper\n//\t\"(*bytes.Buffer).Bytes$thunk\" // thunk (func wrapping method; receiver is param 0)\n//\t\"(*bytes.Buffer).Bytes$bound\" // bound (func wrapping method; receiver supplied by closure)\n//\t\"main.main$1\"                 // an anonymous function in main\n//\t\"main.init#1\"                 // a declared init function\n//\t\"main.init\"                   // the synthesized package initializer\n//\n// When these functions are referred to from within the same package\n// (i.e. from == f.Pkg.Object), they are rendered without the package path.\n// For example: \"IsNaN\", \"(*Buffer).Bytes\", etc.\n//\n// All non-synthetic functions have distinct package-qualified names.\n// (But two methods may have the same name \"(T).f\" if one is a synthetic\n// wrapper promoting a non-exported method \"f\" from another package; in\n// that case, the strings are equal but the identifiers \"f\" are distinct.)\nfunc (f *Function) RelString(from *types.Package) string {\n\t// Anonymous?\n\tif f.parent != nil {\n\t\t// An anonymous function's Name() looks like \"parentName$1\",\n\t\t// but its String() should include the type/package/etc.\n\t\tparent := f.parent.RelString(from)\n\t\tfor i, anon := range f.parent.AnonFuncs {\n\t\t\tif anon == f {\n\t\t\t\treturn fmt.Sprintf(\"%s$%d\", parent, 1+i)\n\t\t\t}\n\t\t}\n\n\t\treturn f.name // should never happen\n\t}\n\n\t// Method (declared or wrapper)?\n\tif recv := f.Signature.Recv(); recv != nil {\n\t\treturn f.relMethod(from, recv.Type())\n\t}\n\n\t// Thunk?\n\tif f.method != nil {\n\t\treturn f.relMethod(from, f.method.Recv())\n\t}\n\n\t// Bound?\n\tif len(f.FreeVars) == 1 && strings.HasSuffix(f.name, \"$bound\") {\n\t\treturn f.relMethod(from, f.FreeVars[0].Type())\n\t}\n\n\t// Package-level function?\n\t// Prefix with package name for cross-package references only.\n\tif p := f.pkg(); p != nil && p != from {\n\t\treturn fmt.Sprintf(\"%s.%s\", p.Path(), f.name)\n\t}\n\n\t// Unknown.\n\treturn f.name\n}\n\nfunc (f *Function) relMethod(from *types.Package, recv types.Type) string {\n\treturn fmt.Sprintf(\"(%s).%s\", relType(recv, from), f.name)\n}\n\n// writeSignature writes to buf the signature sig in declaration syntax.\nfunc writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature) {\n\tbuf.WriteString(\"func \")\n\tif recv := sig.Recv(); recv != nil {\n\t\tbuf.WriteString(\"(\")\n\t\tif name := recv.Name(); name != \"\" {\n\t\t\tbuf.WriteString(name)\n\t\t\tbuf.WriteString(\" \")\n\t\t}\n\t\ttypes.WriteType(buf, recv.Type(), types.RelativeTo(from))\n\t\tbuf.WriteString(\") \")\n\t}\n\tbuf.WriteString(name)\n\ttypes.WriteSignature(buf, sig, types.RelativeTo(from))\n}\n\nfunc (f *Function) pkg() *types.Package {\n\tif f.Pkg != nil {\n\t\treturn f.Pkg.Pkg\n\t}\n\treturn nil\n}\n\nvar _ io.WriterTo = (*Function)(nil) // *Function implements io.Writer\n\nfunc (f *Function) WriteTo(w io.Writer) (int64, error) {\n\tvar buf bytes.Buffer\n\tWriteFunction(&buf, f)\n\tn, err := w.Write(buf.Bytes())\n\treturn int64(n), err\n}\n\n// WriteFunction writes to buf a human-readable \"disassembly\" of f.\nfunc WriteFunction(buf *bytes.Buffer, f *Function) {\n\tfmt.Fprintf(buf, \"# Name: %s\\n\", f.String())\n\tif f.Pkg != nil {\n\t\tfmt.Fprintf(buf, \"# Package: %s\\n\", f.Pkg.Pkg.Path())\n\t}\n\tif syn := f.Synthetic; syn != 0 {\n\t\tfmt.Fprintln(buf, \"# Synthetic:\", syn)\n\t}\n\tif pos := f.Pos(); pos.IsValid() {\n\t\tfmt.Fprintf(buf, \"# Location: %s\\n\", f.Prog.Fset.Position(pos))\n\t}\n\n\tif f.parent != nil {\n\t\tfmt.Fprintf(buf, \"# Parent: %s\\n\", f.parent.Name())\n\t}\n\n\tfrom := f.pkg()\n\n\tif f.FreeVars != nil {\n\t\tbuf.WriteString(\"# Free variables:\\n\")\n\t\tfor i, fv := range f.FreeVars {\n\t\t\tfmt.Fprintf(buf, \"# % 3d:\\t%s %s\\n\", i, fv.Name(), relType(fv.Type(), from))\n\t\t}\n\t}\n\n\tif len(f.Locals) > 0 {\n\t\tbuf.WriteString(\"# Locals:\\n\")\n\t\tfor i, l := range f.Locals {\n\t\t\tfmt.Fprintf(buf, \"# % 3d:\\t%s %s\\n\", i, l.Name(), relType(deref(l.Type()), from))\n\t\t}\n\t}\n\twriteSignature(buf, from, f.Name(), f.Signature)\n\tbuf.WriteString(\":\\n\")\n\n\tif f.Blocks == nil {\n\t\tbuf.WriteString(\"\\t(external)\\n\")\n\t}\n\n\tfor _, b := range f.Blocks {\n\t\tif b == nil {\n\t\t\t// Corrupt CFG.\n\t\t\tfmt.Fprintf(buf, \".nil:\\n\")\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Fprintf(buf, \"b%d:\", b.Index)\n\t\tif len(b.Preds) > 0 {\n\t\t\tfmt.Fprint(buf, \" ←\")\n\t\t\tfor _, pred := range b.Preds {\n\t\t\t\tfmt.Fprintf(buf, \" b%d\", pred.Index)\n\t\t\t}\n\t\t}\n\t\tif b.Comment != \"\" {\n\t\t\tfmt.Fprintf(buf, \" # %s\", b.Comment)\n\t\t}\n\t\tbuf.WriteByte('\\n')\n\n\t\tif false { // CFG debugging\n\t\t\tfmt.Fprintf(buf, \"\\t# CFG: %s --> %s --> %s\\n\", b.Preds, b, b.Succs)\n\t\t}\n\n\t\tbuf2 := &bytes.Buffer{}\n\t\tfor _, instr := range b.Instrs {\n\t\t\tbuf.WriteString(\"\\t\")\n\t\t\tswitch v := instr.(type) {\n\t\t\tcase Value:\n\t\t\t\t// Left-align the instruction.\n\t\t\t\tif name := v.Name(); name != \"\" {\n\t\t\t\t\tfmt.Fprintf(buf, \"%s = \", name)\n\t\t\t\t}\n\t\t\t\tbuf.WriteString(instr.String())\n\t\t\tcase nil:\n\t\t\t\t// Be robust against bad transforms.\n\t\t\t\tbuf.WriteString(\"<deleted>\")\n\t\t\tdefault:\n\t\t\t\tbuf.WriteString(instr.String())\n\t\t\t}\n\t\t\tif instr != nil && instr.Comment() != \"\" {\n\t\t\t\tbuf.WriteString(\" # \")\n\t\t\t\tbuf.WriteString(instr.Comment())\n\t\t\t}\n\t\t\tbuf.WriteString(\"\\n\")\n\n\t\t\tif f.Prog.mode&PrintSource != 0 {\n\t\t\t\tif s := instr.Source(); s != nil {\n\t\t\t\t\tbuf2.Reset()\n\t\t\t\t\tformat.Node(buf2, f.Prog.Fset, s)\n\t\t\t\t\tfor {\n\t\t\t\t\t\tline, err := buf2.ReadString('\\n')\n\t\t\t\t\t\tif len(line) == 0 {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbuf.WriteString(\"\\t\\t> \")\n\t\t\t\t\t\tbuf.WriteString(line)\n\t\t\t\t\t\tif line[len(line)-1] != '\\n' {\n\t\t\t\t\t\t\tbuf.WriteString(\"\\n\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbuf.WriteString(\"\\n\")\n\t}\n}\n\n// newBasicBlock adds to f a new basic block and returns it.  It does\n// not automatically become the current block for subsequent calls to emit.\n// comment is an optional string for more readable debugging output.\nfunc (f *Function) newBasicBlock(comment string) *BasicBlock {\n\tvar instrs []Instruction\n\tif len(f.functionBody.scratchInstructions) > 0 {\n\t\tinstrs = f.functionBody.scratchInstructions[0:0:avgInstructionsPerBlock]\n\t\tf.functionBody.scratchInstructions = f.functionBody.scratchInstructions[avgInstructionsPerBlock:]\n\t} else {\n\t\tinstrs = make([]Instruction, 0, avgInstructionsPerBlock)\n\t}\n\n\tb := &BasicBlock{\n\t\tIndex:   len(f.Blocks),\n\t\tComment: comment,\n\t\tparent:  f,\n\t\tInstrs:  instrs,\n\t}\n\tb.Succs = b.succs2[:0]\n\tf.Blocks = append(f.Blocks, b)\n\treturn b\n}\n\n// NewFunction returns a new synthetic Function instance belonging to\n// prog, with its name and signature fields set as specified.\n//\n// The caller is responsible for initializing the remaining fields of\n// the function object, e.g. Pkg, Params, Blocks.\n//\n// It is practically impossible for clients to construct well-formed\n// IR functions/packages/programs directly, so we assume this is the\n// job of the Builder alone.  NewFunction exists to provide clients a\n// little flexibility.  For example, analysis tools may wish to\n// construct fake Functions for the root of the callgraph, a fake\n// \"reflect\" package, etc.\n//\n// TODO(adonovan): think harder about the API here.\nfunc (prog *Program) NewFunction(name string, sig *types.Signature, provenance Synthetic) *Function {\n\treturn &Function{Prog: prog, name: name, Signature: sig, Synthetic: provenance}\n}\n\n//lint:ignore U1000 we may make use of this for functions loaded from export data\ntype extentNode [2]token.Pos\n\nfunc (n extentNode) Pos() token.Pos { return n[0] }\nfunc (n extentNode) End() token.Pos { return n[1] }\n\nfunc (f *Function) initHTML(name string) {\n\tif name == \"\" {\n\t\treturn\n\t}\n\tif rel := f.RelString(nil); rel == name {\n\t\tf.wr = NewHTMLWriter(\"ir.html\", rel, \"\")\n\t}\n}\n\nfunc killInstruction(instr Instruction) {\n\tops := instr.Operands(nil)\n\tfor _, op := range ops {\n\t\tif refs := (*op).Referrers(); refs != nil {\n\t\t\t*refs = removeInstr(*refs, instr)\n\t\t}\n\t}\n}\n\n// identVar returns the variable defined by id.\nfunc identVar(fn *Function, id *ast.Ident) *types.Var {\n\treturn fn.Pkg.info.Defs[id].(*types.Var)\n}\n\n// unique returns a unique positive int within the source tree of f.\n// The source tree of f includes all of f's ancestors by parent and all\n// of the AnonFuncs contained within these.\nfunc unique(f *Function) int64 {\n\tf.uniq++\n\treturn f.uniq\n}\n\n// exit is a change of control flow going from a range-over-func\n// yield function to an ancestor function caused by a break, continue,\n// goto, or return statement.\n//\n// There are 3 types of exits:\n// * return from the source function (from ReturnStmt),\n// * jump to a block (from break and continue statements [labelled/unlabelled]),\n// * go to a label (from goto statements).\n//\n// As the builder does one pass over the ast, it is unclear whether\n// a forward goto statement will leave a range-over-func body.\n// The function being exited to is unresolved until the end\n// of building the range-over-func body.\ntype exit struct {\n\tid     int64     // unique value for exit within from and to\n\tfrom   *Function // the function the exit starts from\n\tto     *Function // the function being exited to (nil if unresolved)\n\tsource ast.Node\n\n\tblock *BasicBlock  // basic block within to being jumped to.\n\tlabel *types.Label // forward label being jumped to via goto.\n\t// block == nil && label == nil => return\n}\n\n// storeVar emits to function f code to store a value v to a *types.Var x.\nfunc storeVar(f *Function, x *types.Var, v Value, source ast.Node) {\n\temitStore(f, f.lookup(x, true), v, source)\n}\n\n// labelExit creates a new exit to a yield fn to exit the function using a label.\nfunc labelExit(fn *Function, label *types.Label, source ast.Node) *exit {\n\te := &exit{\n\t\tid:     unique(fn),\n\t\tfrom:   fn,\n\t\tto:     nil,\n\t\tsource: source,\n\t\tlabel:  label,\n\t}\n\tfn.exits = append(fn.exits, e)\n\treturn e\n}\n\n// blockExit creates a new exit to a yield fn that jumps to a basic block.\nfunc blockExit(fn *Function, block *BasicBlock, source ast.Node) *exit {\n\te := &exit{\n\t\tid:     unique(fn),\n\t\tfrom:   fn,\n\t\tto:     block.parent,\n\t\tsource: source,\n\t\tblock:  block,\n\t}\n\tfn.exits = append(fn.exits, e)\n\treturn e\n}\n\n// returnExit creates a new exit to a yield fn that returns to the source function.\nfunc returnExit(fn *Function, source ast.Node) *exit {\n\te := &exit{\n\t\tid:     unique(fn),\n\t\tfrom:   fn,\n\t\tto:     fn.sourceFn,\n\t\tsource: source,\n\t}\n\tfn.exits = append(fn.exits, e)\n\treturn e\n}\n"
  },
  {
    "path": "go/ir/html.go",
    "content": "// Copyright 2015 The Go Authors. All rights reserved.\n// Copyright 2019 Dominik Honnef. All rights reserved.\n\npackage ir\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/types\"\n\t\"html\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n)\n\nfunc live(f *Function) []bool {\n\tmax := 0\n\tvar ops []*Value\n\n\tfor _, b := range f.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tif int(instr.ID()) > max {\n\t\t\t\tmax = int(instr.ID())\n\t\t\t}\n\t\t}\n\t}\n\n\tout := make([]bool, max+1)\n\tvar q []Node\n\tfor _, b := range f.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tswitch instr.(type) {\n\t\t\tcase *BlankStore, *Call, *ConstantSwitch, *Defer, *Go, *If, *Jump, *MapUpdate, *Next, *Panic, *Recv, *Return, *RunDefers, *Send, *Store, *Unreachable:\n\t\t\t\tout[instr.ID()] = true\n\t\t\t\tq = append(q, instr)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor len(q) > 0 {\n\t\tv := q[len(q)-1]\n\t\tq = q[:len(q)-1]\n\t\tfor _, op := range v.Operands(ops) {\n\t\t\tif *op == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif !out[(*op).ID()] {\n\t\t\t\tout[(*op).ID()] = true\n\t\t\t\tq = append(q, *op)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out\n}\n\ntype funcPrinter interface {\n\tstartBlock(b *BasicBlock, reachable bool)\n\tendBlock(b *BasicBlock)\n\tvalue(v Node, live bool)\n\tstartDepCycle()\n\tendDepCycle()\n\tnamed(n string, vals []Value)\n}\n\nfunc namedValues(f *Function) map[types.Object][]Value {\n\tnames := map[types.Object][]Value{}\n\tfor _, b := range f.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tif instr, ok := instr.(*DebugRef); ok {\n\t\t\t\tif obj := instr.object; obj != nil {\n\t\t\t\t\tnames[obj] = append(names[obj], instr.X)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// XXX deduplicate values\n\treturn names\n}\n\nfunc fprintFunc(p funcPrinter, f *Function) {\n\t// XXX does our IR form preserve unreachable blocks?\n\t// reachable, live := findlive(f)\n\n\tl := live(f)\n\tfor _, b := range f.Blocks {\n\t\t// XXX\n\t\t// p.startBlock(b, reachable[b.Index])\n\t\tp.startBlock(b, true)\n\n\t\tend := max(len(b.Instrs)-1, 0)\n\t\tfor _, v := range b.Instrs[:end] {\n\t\t\tif _, ok := v.(*DebugRef); !ok {\n\t\t\t\tp.value(v, l[v.ID()])\n\t\t\t}\n\t\t}\n\t\tp.endBlock(b)\n\t}\n\n\tnames := namedValues(f)\n\tkeys := make([]types.Object, 0, len(names))\n\tfor key := range names {\n\t\tkeys = append(keys, key)\n\t}\n\tsort.Slice(keys, func(i, j int) bool {\n\t\treturn keys[i].Pos() < keys[j].Pos()\n\t})\n\tfor _, key := range keys {\n\t\tp.named(key.Name(), names[key])\n\t}\n}\n\nfunc opName(v Node) string {\n\tswitch v := v.(type) {\n\tcase *Call:\n\t\tif v.Common().IsInvoke() {\n\t\t\treturn \"Invoke\"\n\t\t}\n\t\treturn \"Call\"\n\tcase *Alloc:\n\t\tif v.Heap {\n\t\t\treturn \"HeapAlloc\"\n\t\t}\n\t\treturn \"StackAlloc\"\n\tcase *Select:\n\t\tif v.Blocking {\n\t\t\treturn \"SelectBlocking\"\n\t\t}\n\t\treturn \"SelectNonBlocking\"\n\tdefault:\n\t\treturn reflect.ValueOf(v).Type().Elem().Name()\n\t}\n}\n\ntype HTMLWriter struct {\n\tw    io.WriteCloser\n\tpath string\n\tdot  *dotWriter\n}\n\nfunc NewHTMLWriter(path string, funcname, cfgMask string) *HTMLWriter {\n\tout, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)\n\tif err != nil {\n\t\tlog.Fatalf(\"%v\", err)\n\t}\n\tpwd, err := os.Getwd()\n\tif err != nil {\n\t\tlog.Fatalf(\"%v\", err)\n\t}\n\thtml := HTMLWriter{w: out, path: filepath.Join(pwd, path)}\n\thtml.dot = newDotWriter()\n\thtml.start(funcname)\n\treturn &html\n}\n\nfunc (w *HTMLWriter) start(name string) {\n\tif w == nil {\n\t\treturn\n\t}\n\tw.WriteString(\"<html>\")\n\tw.WriteString(`<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">\n<style>\n\nbody {\n    font-size: 14px;\n    font-family: Arial, sans-serif;\n}\n\nh1 {\n    font-size: 18px;\n    display: inline-block;\n    margin: 0 1em .5em 0;\n}\n\n#helplink {\n    display: inline-block;\n}\n\n#help {\n    display: none;\n}\n\n.stats {\n    font-size: 60%;\n}\n\ntable {\n    border: 1px solid black;\n    table-layout: fixed;\n    width: 300px;\n}\n\nth, td {\n    border: 1px solid black;\n    overflow: hidden;\n    width: 400px;\n    vertical-align: top;\n    padding: 5px;\n}\n\ntd > h2 {\n    cursor: pointer;\n    font-size: 120%;\n}\n\ntd.collapsed {\n    font-size: 12px;\n    width: 12px;\n    border: 0px;\n    padding: 0;\n    cursor: pointer;\n    background: #fafafa;\n}\n\ntd.collapsed  div {\n     -moz-transform: rotate(-90.0deg);  /* FF3.5+ */\n       -o-transform: rotate(-90.0deg);  /* Opera 10.5 */\n  -webkit-transform: rotate(-90.0deg);  /* Saf3.1+, Chrome */\n             filter:  progid:DXImageTransform.Microsoft.BasicImage(rotation=0.083);  /* IE6,IE7 */\n         -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=0.083)\"; /* IE8 */\n         margin-top: 10.3em;\n         margin-left: -10em;\n         margin-right: -10em;\n         text-align: right;\n}\n\ncode, pre, .lines, .ast {\n    font-family: Menlo, monospace;\n    font-size: 12px;\n}\n\npre {\n    -moz-tab-size: 4;\n    -o-tab-size:   4;\n    tab-size:      4;\n}\n\n.allow-x-scroll {\n    overflow-x: scroll;\n}\n\n.lines {\n    float: left;\n    overflow: hidden;\n    text-align: right;\n}\n\n.lines div {\n    padding-right: 10px;\n    color: gray;\n}\n\ndiv.line-number {\n    font-size: 12px;\n}\n\n.ast {\n    white-space: nowrap;\n}\n\ntd.ssa-prog {\n    width: 600px;\n    word-wrap: break-word;\n}\n\nli {\n    list-style-type: none;\n}\n\nli.ssa-long-value {\n    text-indent: -2em;  /* indent wrapped lines */\n}\n\nli.ssa-value-list {\n    display: inline;\n}\n\nli.ssa-start-block {\n    padding: 0;\n    margin: 0;\n}\n\nli.ssa-end-block {\n    padding: 0;\n    margin: 0;\n}\n\nul.ssa-print-func {\n    padding-left: 0;\n}\n\nli.ssa-start-block button {\n    padding: 0 1em;\n    margin: 0;\n    border: none;\n    display: inline;\n    font-size: 14px;\n    float: right;\n}\n\nbutton:hover {\n    background-color: #eee;\n    cursor: pointer;\n}\n\ndl.ssa-gen {\n    padding-left: 0;\n}\n\ndt.ssa-prog-src {\n    padding: 0;\n    margin: 0;\n    float: left;\n    width: 4em;\n}\n\ndd.ssa-prog {\n    padding: 0;\n    margin-right: 0;\n    margin-left: 4em;\n}\n\n.dead-value {\n    color: gray;\n}\n\n.dead-block {\n    opacity: 0.5;\n}\n\n.depcycle {\n    font-style: italic;\n}\n\n.line-number {\n    font-size: 11px;\n}\n\n.no-line-number {\n    font-size: 11px;\n    color: gray;\n}\n\n.zoom {\n\tposition: absolute;\n\tfloat: left;\n\twhite-space: nowrap;\n\tbackground-color: #eee;\n}\n\n.zoom a:link, .zoom a:visited  {\n    text-decoration: none;\n    color: blue;\n    font-size: 16px;\n    padding: 4px 2px;\n}\n\nsvg {\n    cursor: default;\n    outline: 1px solid #eee;\n}\n\n.highlight-aquamarine     { background-color: aquamarine; }\n.highlight-coral          { background-color: coral; }\n.highlight-lightpink      { background-color: lightpink; }\n.highlight-lightsteelblue { background-color: lightsteelblue; }\n.highlight-palegreen      { background-color: palegreen; }\n.highlight-skyblue        { background-color: skyblue; }\n.highlight-lightgray      { background-color: lightgray; }\n.highlight-yellow         { background-color: yellow; }\n.highlight-lime           { background-color: lime; }\n.highlight-khaki          { background-color: khaki; }\n.highlight-aqua           { background-color: aqua; }\n.highlight-salmon         { background-color: salmon; }\n\n.outline-blue           { outline: blue solid 2px; }\n.outline-red            { outline: red solid 2px; }\n.outline-blueviolet     { outline: blueviolet solid 2px; }\n.outline-darkolivegreen { outline: darkolivegreen solid 2px; }\n.outline-fuchsia        { outline: fuchsia solid 2px; }\n.outline-sienna         { outline: sienna solid 2px; }\n.outline-gold           { outline: gold solid 2px; }\n.outline-orangered      { outline: orangered solid 2px; }\n.outline-teal           { outline: teal solid 2px; }\n.outline-maroon         { outline: maroon solid 2px; }\n.outline-black          { outline: black solid 2px; }\n\nellipse.outline-blue           { stroke-width: 2px; stroke: blue; }\nellipse.outline-red            { stroke-width: 2px; stroke: red; }\nellipse.outline-blueviolet     { stroke-width: 2px; stroke: blueviolet; }\nellipse.outline-darkolivegreen { stroke-width: 2px; stroke: darkolivegreen; }\nellipse.outline-fuchsia        { stroke-width: 2px; stroke: fuchsia; }\nellipse.outline-sienna         { stroke-width: 2px; stroke: sienna; }\nellipse.outline-gold           { stroke-width: 2px; stroke: gold; }\nellipse.outline-orangered      { stroke-width: 2px; stroke: orangered; }\nellipse.outline-teal           { stroke-width: 2px; stroke: teal; }\nellipse.outline-maroon         { stroke-width: 2px; stroke: maroon; }\nellipse.outline-black          { stroke-width: 2px; stroke: black; }\n\n</style>\n\n<script type=\"text/javascript\">\n// ordered list of all available highlight colors\nvar highlights = [\n    \"highlight-aquamarine\",\n    \"highlight-coral\",\n    \"highlight-lightpink\",\n    \"highlight-lightsteelblue\",\n    \"highlight-palegreen\",\n    \"highlight-skyblue\",\n    \"highlight-lightgray\",\n    \"highlight-yellow\",\n    \"highlight-lime\",\n    \"highlight-khaki\",\n    \"highlight-aqua\",\n    \"highlight-salmon\"\n];\n\n// state: which value is highlighted this color?\nvar highlighted = {};\nfor (var i = 0; i < highlights.length; i++) {\n    highlighted[highlights[i]] = \"\";\n}\n\n// ordered list of all available outline colors\nvar outlines = [\n    \"outline-blue\",\n    \"outline-red\",\n    \"outline-blueviolet\",\n    \"outline-darkolivegreen\",\n    \"outline-fuchsia\",\n    \"outline-sienna\",\n    \"outline-gold\",\n    \"outline-orangered\",\n    \"outline-teal\",\n    \"outline-maroon\",\n    \"outline-black\"\n];\n\n// state: which value is outlined this color?\nvar outlined = {};\nfor (var i = 0; i < outlines.length; i++) {\n    outlined[outlines[i]] = \"\";\n}\n\nwindow.onload = function() {\n    var ssaElemClicked = function(elem, event, selections, selected) {\n        event.stopPropagation();\n\n        // TODO: pushState with updated state and read it on page load,\n        // so that state can survive across reloads\n\n        // find all values with the same name\n        var c = elem.classList.item(0);\n        var x = document.getElementsByClassName(c);\n\n        // if selected, remove selections from all of them\n        // otherwise, attempt to add\n\n        var remove = \"\";\n        for (var i = 0; i < selections.length; i++) {\n            var color = selections[i];\n            if (selected[color] == c) {\n                remove = color;\n                break;\n            }\n        }\n\n        if (remove != \"\") {\n            for (var i = 0; i < x.length; i++) {\n                x[i].classList.remove(remove);\n            }\n            selected[remove] = \"\";\n            return;\n        }\n\n        // we're adding a selection\n        // find first available color\n        var avail = \"\";\n        for (var i = 0; i < selections.length; i++) {\n            var color = selections[i];\n            if (selected[color] == \"\") {\n                avail = color;\n                break;\n            }\n        }\n        if (avail == \"\") {\n            alert(\"out of selection colors; go add more\");\n            return;\n        }\n\n        // set that as the selection\n        for (var i = 0; i < x.length; i++) {\n            x[i].classList.add(avail);\n        }\n        selected[avail] = c;\n    };\n\n    var ssaValueClicked = function(event) {\n        ssaElemClicked(this, event, highlights, highlighted);\n    };\n\n    var ssaBlockClicked = function(event) {\n        ssaElemClicked(this, event, outlines, outlined);\n    };\n\n    var ssavalues = document.getElementsByClassName(\"ssa-value\");\n    for (var i = 0; i < ssavalues.length; i++) {\n        ssavalues[i].addEventListener('click', ssaValueClicked);\n    }\n\n    var ssalongvalues = document.getElementsByClassName(\"ssa-long-value\");\n    for (var i = 0; i < ssalongvalues.length; i++) {\n        // don't attach listeners to li nodes, just the spans they contain\n        if (ssalongvalues[i].nodeName == \"SPAN\") {\n            ssalongvalues[i].addEventListener('click', ssaValueClicked);\n        }\n    }\n\n    var ssablocks = document.getElementsByClassName(\"ssa-block\");\n    for (var i = 0; i < ssablocks.length; i++) {\n        ssablocks[i].addEventListener('click', ssaBlockClicked);\n    }\n\n    var lines = document.getElementsByClassName(\"line-number\");\n    for (var i = 0; i < lines.length; i++) {\n        lines[i].addEventListener('click', ssaValueClicked);\n    }\n\n    // Contains phase names which are expanded by default. Other columns are collapsed.\n    var expandedDefault = [\n        \"start\",\n        \"deadcode\",\n        \"opt\",\n        \"lower\",\n        \"late deadcode\",\n        \"regalloc\",\n        \"genssa\",\n    ];\n\n    function toggler(phase) {\n        return function() {\n            toggle_cell(phase+'-col');\n            toggle_cell(phase+'-exp');\n        };\n    }\n\n    function toggle_cell(id) {\n        var e = document.getElementById(id);\n        if (e.style.display == 'table-cell') {\n            e.style.display = 'none';\n        } else {\n            e.style.display = 'table-cell';\n        }\n    }\n\n    // Go through all columns and collapse needed phases.\n    var td = document.getElementsByTagName(\"td\");\n    for (var i = 0; i < td.length; i++) {\n        var id = td[i].id;\n        var phase = id.substr(0, id.length-4);\n        var show = expandedDefault.indexOf(phase) !== -1\n        if (id.endsWith(\"-exp\")) {\n            var h2 = td[i].getElementsByTagName(\"h2\");\n            if (h2 && h2[0]) {\n                h2[0].addEventListener('click', toggler(phase));\n            }\n        } else {\n            td[i].addEventListener('click', toggler(phase));\n        }\n        if (id.endsWith(\"-col\") && show || id.endsWith(\"-exp\") && !show) {\n            td[i].style.display = 'none';\n            continue;\n        }\n        td[i].style.display = 'table-cell';\n    }\n\n    // find all svg block nodes, add their block classes\n    var nodes = document.querySelectorAll('*[id^=\"graph_node_\"]');\n    for (var i = 0; i < nodes.length; i++) {\n    \tvar node = nodes[i];\n    \tvar name = node.id.toString();\n    \tvar block = name.substring(name.lastIndexOf(\"_\")+1);\n    \tnode.classList.remove(\"node\");\n    \tnode.classList.add(block);\n        node.addEventListener('click', ssaBlockClicked);\n        var ellipse = node.getElementsByTagName('ellipse')[0];\n        ellipse.classList.add(block);\n        ellipse.addEventListener('click', ssaBlockClicked);\n    }\n\n    // make big graphs smaller\n    var targetScale = 0.5;\n    var nodes = document.querySelectorAll('*[id^=\"svg_graph_\"]');\n    // TODO: Implement smarter auto-zoom using the viewBox attribute\n    // and in case of big graphs set the width and height of the svg graph to\n    // maximum allowed.\n    for (var i = 0; i < nodes.length; i++) {\n    \tvar node = nodes[i];\n    \tvar name = node.id.toString();\n    \tvar phase = name.substring(name.lastIndexOf(\"_\")+1);\n    \tvar gNode = document.getElementById(\"g_graph_\"+phase);\n    \tvar scale = gNode.transform.baseVal.getItem(0).matrix.a;\n    \tif (scale > targetScale) {\n    \t\tnode.width.baseVal.value *= targetScale / scale;\n    \t\tnode.height.baseVal.value *= targetScale / scale;\n    \t}\n    }\n};\n\nfunction toggle_visibility(id) {\n    var e = document.getElementById(id);\n    if (e.style.display == 'block') {\n        e.style.display = 'none';\n    } else {\n        e.style.display = 'block';\n    }\n}\n\nfunction hideBlock(el) {\n    var es = el.parentNode.parentNode.getElementsByClassName(\"ssa-value-list\");\n    if (es.length===0)\n        return;\n    var e = es[0];\n    if (e.style.display === 'block' || e.style.display === '') {\n        e.style.display = 'none';\n        el.innerHTML = '+';\n    } else {\n        e.style.display = 'block';\n        el.innerHTML = '-';\n    }\n}\n\n// TODO: scale the graph with the viewBox attribute.\nfunction graphReduce(id) {\n    var node = document.getElementById(id);\n    if (node) {\n    \t\tnode.width.baseVal.value *= 0.9;\n    \t\tnode.height.baseVal.value *= 0.9;\n    }\n    return false;\n}\n\nfunction graphEnlarge(id) {\n    var node = document.getElementById(id);\n    if (node) {\n    \t\tnode.width.baseVal.value *= 1.1;\n    \t\tnode.height.baseVal.value *= 1.1;\n    }\n    return false;\n}\n\nfunction makeDraggable(event) {\n    var svg = event.target;\n    if (window.PointerEvent) {\n        svg.addEventListener('pointerdown', startDrag);\n        svg.addEventListener('pointermove', drag);\n        svg.addEventListener('pointerup', endDrag);\n        svg.addEventListener('pointerleave', endDrag);\n    } else {\n        svg.addEventListener('mousedown', startDrag);\n        svg.addEventListener('mousemove', drag);\n        svg.addEventListener('mouseup', endDrag);\n        svg.addEventListener('mouseleave', endDrag);\n    }\n\n    var point = svg.createSVGPoint();\n    var isPointerDown = false;\n    var pointerOrigin;\n    var viewBox = svg.viewBox.baseVal;\n\n    function getPointFromEvent (event) {\n        point.x = event.clientX;\n        point.y = event.clientY;\n\n        // We get the current transformation matrix of the SVG and we inverse it\n        var invertedSVGMatrix = svg.getScreenCTM().inverse();\n        return point.matrixTransform(invertedSVGMatrix);\n    }\n\n    function startDrag(event) {\n        isPointerDown = true;\n        pointerOrigin = getPointFromEvent(event);\n    }\n\n    function drag(event) {\n        if (!isPointerDown) {\n            return;\n        }\n        event.preventDefault();\n\n        var pointerPosition = getPointFromEvent(event);\n        viewBox.x -= (pointerPosition.x - pointerOrigin.x);\n        viewBox.y -= (pointerPosition.y - pointerOrigin.y);\n    }\n\n    function endDrag(event) {\n        isPointerDown = false;\n    }\n}</script>\n\n</head>`)\n\tw.WriteString(\"<body>\")\n\tw.WriteString(\"<h1>\")\n\tw.WriteString(html.EscapeString(name))\n\tw.WriteString(\"</h1>\")\n\tw.WriteString(`\n<a href=\"#\" onclick=\"toggle_visibility('help');return false;\" id=\"helplink\">help</a>\n<div id=\"help\">\n\n<p>\nClick on a value or block to toggle highlighting of that value/block\nand its uses.  (Values and blocks are highlighted by ID, and IDs of\ndead items may be reused, so not all highlights necessarily correspond\nto the clicked item.)\n</p>\n\n<p>\nFaded out values and blocks are dead code that has not been eliminated.\n</p>\n\n<p>\nValues printed in italics have a dependency cycle.\n</p>\n\n<p>\n<b>CFG</b>: Dashed edge is for unlikely branches. Blue color is for backward edges.\nEdge with a dot means that this edge follows the order in which blocks were laid out.\n</p>\n\n</div>\n`)\n\tw.WriteString(\"<table>\")\n\tw.WriteString(\"<tr>\")\n}\n\nfunc (w *HTMLWriter) Close() {\n\tif w == nil {\n\t\treturn\n\t}\n\tio.WriteString(w.w, \"</tr>\")\n\tio.WriteString(w.w, \"</table>\")\n\tio.WriteString(w.w, \"</body>\")\n\tio.WriteString(w.w, \"</html>\")\n\tw.w.Close()\n\tfmt.Printf(\"dumped IR to %v\\n\", w.path)\n}\n\n// WriteFunc writes f in a column headed by title.\n// phase is used for collapsing columns and should be unique across the table.\nfunc (w *HTMLWriter) WriteFunc(phase, title string, f *Function) {\n\tif w == nil {\n\t\treturn\n\t}\n\tw.WriteColumn(phase, title, \"\", funcHTML(f, phase, w.dot))\n}\n\n// WriteColumn writes raw HTML in a column headed by title.\n// It is intended for pre- and post-compilation log output.\nfunc (w *HTMLWriter) WriteColumn(phase, title, class, html string) {\n\tif w == nil {\n\t\treturn\n\t}\n\tid := strings.Replace(phase, \" \", \"-\", -1)\n\t// collapsed column\n\tw.Printf(\"<td id=\\\"%v-col\\\" class=\\\"collapsed\\\"><div>%v</div></td>\", id, phase)\n\n\tif class == \"\" {\n\t\tw.Printf(\"<td id=\\\"%v-exp\\\">\", id)\n\t} else {\n\t\tw.Printf(\"<td id=\\\"%v-exp\\\" class=\\\"%v\\\">\", id, class)\n\t}\n\tw.WriteString(\"<h2>\" + title + \"</h2>\")\n\tw.WriteString(html)\n\tw.WriteString(\"</td>\")\n}\n\nfunc (w *HTMLWriter) Printf(msg string, v ...any) {\n\tif _, err := fmt.Fprintf(w.w, msg, v...); err != nil {\n\t\tlog.Fatalf(\"%v\", err)\n\t}\n}\n\nfunc (w *HTMLWriter) WriteString(s string) {\n\tif _, err := io.WriteString(w.w, s); err != nil {\n\t\tlog.Fatalf(\"%v\", err)\n\t}\n}\n\nfunc valueHTML(v Node) string {\n\tif v == nil {\n\t\treturn \"&lt;nil&gt;\"\n\t}\n\t// TODO: Using the value ID as the class ignores the fact\n\t// that value IDs get recycled and that some values\n\t// are transmuted into other values.\n\tclass := fmt.Sprintf(\"t%d\", v.ID())\n\tvar label string\n\tswitch v := v.(type) {\n\tcase *Function:\n\t\tlabel = v.RelString(nil)\n\tcase *Builtin:\n\t\tlabel = v.Name()\n\tdefault:\n\t\tlabel = class\n\t}\n\treturn fmt.Sprintf(\"<span class=\\\"%s ssa-value\\\">%s</span>\", class, label)\n}\n\nfunc valueLongHTML(v Node) string {\n\t// TODO: Any intra-value formatting?\n\t// I'm wary of adding too much visual noise,\n\t// but a little bit might be valuable.\n\t// We already have visual noise in the form of punctuation\n\t// maybe we could replace some of that with formatting.\n\tvar s strings.Builder\n\ts.WriteString(fmt.Sprintf(\"<span class=\\\"t%d ssa-long-value\\\">\", v.ID()))\n\n\tlinenumber := \"<span class=\\\"no-line-number\\\">(?)</span>\"\n\tif v.Pos().IsValid() {\n\t\tline := v.Parent().Prog.Fset.Position(v.Pos()).Line\n\t\tlinenumber = fmt.Sprintf(\"<span class=\\\"l%v line-number\\\">(%d)</span>\", line, line)\n\t}\n\n\ts.WriteString(fmt.Sprintf(\"%s %s = %s\", valueHTML(v), linenumber, opName(v)))\n\n\tif v, ok := v.(Value); ok {\n\t\ts.WriteString(\" &lt;\" + html.EscapeString(v.Type().String()) + \"&gt;\")\n\t}\n\n\tswitch v := v.(type) {\n\tcase *Parameter:\n\t\ts.WriteString(fmt.Sprintf(\" {%s}\", html.EscapeString(v.name)))\n\tcase *BinOp:\n\t\ts.WriteString(fmt.Sprintf(\" {%s}\", html.EscapeString(v.Op.String())))\n\tcase *UnOp:\n\t\ts.WriteString(fmt.Sprintf(\" {%s}\", html.EscapeString(v.Op.String())))\n\tcase *Extract:\n\t\tname := v.Tuple.Type().(*types.Tuple).At(v.Index).Name()\n\t\ts.WriteString(fmt.Sprintf(\" [%d] (%s)\", v.Index, name))\n\tcase *Field:\n\t\tst := v.X.Type().Underlying().(*types.Struct)\n\t\t// Be robust against a bad index.\n\t\tname := \"?\"\n\t\tif 0 <= v.Field && v.Field < st.NumFields() {\n\t\t\tname = st.Field(v.Field).Name()\n\t\t}\n\t\ts.WriteString(fmt.Sprintf(\" [%d] (%s)\", v.Field, name))\n\tcase *FieldAddr:\n\t\tst := deref(v.X.Type()).Underlying().(*types.Struct)\n\t\t// Be robust against a bad index.\n\t\tname := \"?\"\n\t\tif 0 <= v.Field && v.Field < st.NumFields() {\n\t\t\tname = st.Field(v.Field).Name()\n\t\t}\n\n\t\ts.WriteString(fmt.Sprintf(\" [%d] (%s)\", v.Field, name))\n\tcase *Recv:\n\t\ts.WriteString(fmt.Sprintf(\" {%t}\", v.CommaOk))\n\tcase *Call:\n\t\tif v.Common().IsInvoke() {\n\t\t\ts.WriteString(fmt.Sprintf(\" {%s}\", html.EscapeString(v.Common().Method.FullName())))\n\t\t}\n\tcase *Const:\n\t\tif v.Value == nil {\n\t\t\ts.WriteString(\" {&lt;nil&gt;}\")\n\t\t} else {\n\t\t\ts.WriteString(fmt.Sprintf(\" {%s}\", html.EscapeString(v.Value.String())))\n\t\t}\n\tcase *Sigma:\n\t\ts.WriteString(fmt.Sprintf(\" [#%s]\", v.From))\n\t}\n\tfor _, a := range v.Operands(nil) {\n\t\ts.WriteString(fmt.Sprintf(\" %s\", valueHTML(*a)))\n\t}\n\tif v, ok := v.(Instruction); ok {\n\t\ts.WriteString(fmt.Sprintf(\" (%s)\", v.Comment()))\n\t}\n\n\t// OPT(dh): we're calling namedValues many times on the same function.\n\tallNames := namedValues(v.Parent())\n\tvar names []string\n\tfor name, values := range allNames {\n\t\tfor _, value := range values {\n\t\t\tif v == value {\n\t\t\t\tnames = append(names, name.Name())\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif len(names) != 0 {\n\t\ts.WriteString(\" (\" + strings.Join(names, \", \") + \")\")\n\t}\n\n\ts.WriteString(\"</span>\")\n\treturn s.String()\n}\n\nfunc blockHTML(b *BasicBlock) string {\n\t// TODO: Using the value ID as the class ignores the fact\n\t// that value IDs get recycled and that some values\n\t// are transmuted into other values.\n\ts := html.EscapeString(b.String())\n\treturn fmt.Sprintf(\"<span class=\\\"%s ssa-block\\\">%s</span>\", s, s)\n}\n\nfunc blockLongHTML(b *BasicBlock) string {\n\tvar kind string\n\tvar term Instruction\n\tif len(b.Instrs) > 0 {\n\t\tterm = b.Control()\n\t\tkind = opName(term)\n\t}\n\t// TODO: improve this for HTML?\n\tvar s strings.Builder\n\ts.WriteString(fmt.Sprintf(\"<span class=\\\"b%d ssa-block\\\">%s</span>\", b.Index, kind))\n\n\tif term != nil {\n\t\tops := term.Operands(nil)\n\t\tif len(ops) > 0 {\n\t\t\tvar ss []string\n\t\t\tfor _, op := range ops {\n\t\t\t\tss = append(ss, valueHTML(*op))\n\t\t\t}\n\t\t\ts.WriteString(\" \" + strings.Join(ss, \", \"))\n\t\t}\n\t}\n\tif len(b.Succs) > 0 {\n\t\ts.WriteString(\" &#8594;\") // right arrow\n\t\tfor _, c := range b.Succs {\n\t\t\ts.WriteString(\" \" + blockHTML(c))\n\t\t}\n\t}\n\treturn s.String()\n}\n\nfunc funcHTML(f *Function, phase string, dot *dotWriter) string {\n\tbuf := new(bytes.Buffer)\n\tif dot != nil {\n\t\tdot.writeFuncSVG(buf, phase, f)\n\t}\n\tfmt.Fprint(buf, \"<code>\")\n\tp := htmlFuncPrinter{w: buf}\n\tfprintFunc(p, f)\n\n\t// fprintFunc(&buf, f) // TODO: HTML, not text, <br /> for line breaks, etc.\n\tfmt.Fprint(buf, \"</code>\")\n\treturn buf.String()\n}\n\ntype htmlFuncPrinter struct {\n\tw io.Writer\n}\n\nfunc (p htmlFuncPrinter) startBlock(b *BasicBlock, reachable bool) {\n\tvar dead string\n\tif !reachable {\n\t\tdead = \"dead-block\"\n\t}\n\tfmt.Fprintf(p.w, \"<ul class=\\\"%s ssa-print-func %s\\\">\", b, dead)\n\tfmt.Fprintf(p.w, \"<li class=\\\"ssa-start-block\\\">%s:\", blockHTML(b))\n\tif len(b.Preds) > 0 {\n\t\tio.WriteString(p.w, \" &#8592;\") // left arrow\n\t\tfor _, pred := range b.Preds {\n\t\t\tfmt.Fprintf(p.w, \" %s\", blockHTML(pred))\n\t\t}\n\t}\n\tif len(b.Instrs) > 0 {\n\t\tio.WriteString(p.w, `<button onclick=\"hideBlock(this)\">-</button>`)\n\t}\n\tio.WriteString(p.w, \"</li>\")\n\tif len(b.Instrs) > 0 { // start list of values\n\t\tio.WriteString(p.w, \"<li class=\\\"ssa-value-list\\\">\")\n\t\tio.WriteString(p.w, \"<ul>\")\n\t}\n}\n\nfunc (p htmlFuncPrinter) endBlock(b *BasicBlock) {\n\tif len(b.Instrs) > 0 { // end list of values\n\t\tio.WriteString(p.w, \"</ul>\")\n\t\tio.WriteString(p.w, \"</li>\")\n\t}\n\tio.WriteString(p.w, \"<li class=\\\"ssa-end-block\\\">\")\n\tfmt.Fprint(p.w, blockLongHTML(b))\n\tio.WriteString(p.w, \"</li>\")\n\tio.WriteString(p.w, \"</ul>\")\n}\n\nfunc (p htmlFuncPrinter) value(v Node, live bool) {\n\tvar dead string\n\tif !live {\n\t\tdead = \"dead-value\"\n\t}\n\tfmt.Fprintf(p.w, \"<li class=\\\"ssa-long-value %s\\\">\", dead)\n\tfmt.Fprint(p.w, valueLongHTML(v))\n\tio.WriteString(p.w, \"</li>\")\n}\n\nfunc (p htmlFuncPrinter) startDepCycle() {\n\tfmt.Fprintln(p.w, \"<span class=\\\"depcycle\\\">\")\n}\n\nfunc (p htmlFuncPrinter) endDepCycle() {\n\tfmt.Fprintln(p.w, \"</span>\")\n}\n\nfunc (p htmlFuncPrinter) named(n string, vals []Value) {\n\tfmt.Fprintf(p.w, \"<li>name %s: \", n)\n\tfor _, val := range vals {\n\t\tfmt.Fprintf(p.w, \"%s \", valueHTML(val))\n\t}\n\tfmt.Fprintf(p.w, \"</li>\")\n}\n\ntype dotWriter struct {\n\tpath   string\n\tbroken bool\n}\n\n// newDotWriter returns non-nil value when mask is valid.\n// dotWriter will generate SVGs only for the phases specified in the mask.\n// mask can contain following patterns and combinations of them:\n// *   - all of them;\n// x-y - x through y, inclusive;\n// x,y - x and y, but not the passes between.\nfunc newDotWriter() *dotWriter {\n\tpath, err := exec.LookPath(\"dot\")\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn nil\n\t}\n\treturn &dotWriter{path: path}\n}\n\nfunc (d *dotWriter) writeFuncSVG(w io.Writer, phase string, f *Function) {\n\tif d.broken {\n\t\treturn\n\t}\n\tcmd := exec.Command(d.path, \"-Tsvg\")\n\tpipe, err := cmd.StdinPipe()\n\tif err != nil {\n\t\td.broken = true\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tbuf := new(bytes.Buffer)\n\tcmd.Stdout = buf\n\tbufErr := new(bytes.Buffer)\n\tcmd.Stderr = bufErr\n\terr = cmd.Start()\n\tif err != nil {\n\t\td.broken = true\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tfmt.Fprint(pipe, `digraph \"\" { margin=0; size=\"4,40\"; ranksep=.2; `)\n\tid := strings.Replace(phase, \" \", \"-\", -1)\n\tfmt.Fprintf(pipe, `id=\"g_graph_%s\";`, id)\n\tfmt.Fprintf(pipe, `node [style=filled,fillcolor=white,fontsize=16,fontname=\"Menlo,Times,serif\",margin=\"0.01,0.03\"];`)\n\tfmt.Fprintf(pipe, `edge [fontsize=16,fontname=\"Menlo,Times,serif\"];`)\n\tfor _, b := range f.Blocks {\n\t\tlayout := \"\"\n\t\tfmt.Fprintf(pipe, `%v [label=\"%v%s\\n%v\",id=\"graph_node_%v_%v\"];`, b, b, layout, b.Control().String(), id, b)\n\t}\n\tindexOf := make([]int, len(f.Blocks))\n\tfor i, b := range f.Blocks {\n\t\tindexOf[b.Index] = i\n\t}\n\n\t// XXX\n\t/*\n\t\tponums := make([]int32, len(f.Blocks))\n\t\t_ = postorderWithNumbering(f, ponums)\n\t\tisBackEdge := func(from, to int) bool {\n\t\t\treturn ponums[from] <= ponums[to]\n\t\t}\n\t*/\n\tisBackEdge := func(from, to int) bool { return false }\n\n\tfor _, b := range f.Blocks {\n\t\tfor i, s := range b.Succs {\n\t\t\tstyle := \"solid\"\n\t\t\tcolor := \"black\"\n\t\t\tarrow := \"vee\"\n\t\t\tif isBackEdge(b.Index, s.Index) {\n\t\t\t\tcolor = \"blue\"\n\t\t\t}\n\t\t\tfmt.Fprintf(pipe, `%v -> %v [label=\" %d \",style=\"%s\",color=\"%s\",arrowhead=\"%s\"];`, b, s, i, style, color, arrow)\n\t\t}\n\t}\n\tfmt.Fprint(pipe, \"}\")\n\tpipe.Close()\n\terr = cmd.Wait()\n\tif err != nil {\n\t\td.broken = true\n\t\tfmt.Printf(\"dot: %v\\n%v\\n\", err, bufErr.String())\n\t\treturn\n\t}\n\n\tsvgID := \"svg_graph_\" + id\n\tfmt.Fprintf(w, `<div class=\"zoom\"><button onclick=\"return graphReduce('%s');\">-</button> <button onclick=\"return graphEnlarge('%s');\">+</button></div>`, svgID, svgID)\n\t// For now, an awful hack: edit the html as it passes through\n\t// our fingers, finding '<svg ' and injecting needed attributes after it.\n\terr = d.copyUntil(w, buf, `<svg `)\n\tif err != nil {\n\t\tfmt.Printf(\"injecting attributes: %v\\n\", err)\n\t\treturn\n\t}\n\tfmt.Fprintf(w, ` id=\"%s\" onload=\"makeDraggable(evt)\" width=\"100%%\" `, svgID)\n\tio.Copy(w, buf)\n}\n\nfunc (d *dotWriter) copyUntil(w io.Writer, buf *bytes.Buffer, sep string) error {\n\ti := bytes.Index(buf.Bytes(), []byte(sep))\n\tif i == -1 {\n\t\treturn fmt.Errorf(\"couldn't find dot sep %q\", sep)\n\t}\n\t_, err := io.CopyN(w, buf, int64(i+len(sep)))\n\treturn err\n}\n"
  },
  {
    "path": "go/ir/irutil/load.go",
    "content": "// Copyright 2015 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage irutil\n\n// This file defines utility functions for constructing programs in IR form.\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/go/ir\"\n\n\t//lint:ignore SA1019 go/loader is deprecated, but works fine for our tests\n\t\"golang.org/x/tools/go/loader\"\n\t\"golang.org/x/tools/go/packages\"\n)\n\ntype Options struct {\n\t// Which function, if any, to print in HTML form\n\tPrintFunc string\n}\n\n// Packages creates an IR program for a set of packages.\n//\n// The packages must have been loaded from source syntax using the\n// golang.org/x/tools/go/packages.Load function in LoadSyntax or\n// LoadAllSyntax mode.\n//\n// Packages creates an IR package for each well-typed package in the\n// initial list, plus all their dependencies. The resulting list of\n// packages corresponds to the list of initial packages, and may contain\n// a nil if IR code could not be constructed for the corresponding initial\n// package due to type errors.\n//\n// Code for bodies of functions is not built until Build is called on\n// the resulting Program. IR code is constructed only for the initial\n// packages with well-typed syntax trees.\n//\n// The mode parameter controls diagnostics and checking during IR construction.\nfunc Packages(initial []*packages.Package, mode ir.BuilderMode, opts *Options) (*ir.Program, []*ir.Package) {\n\treturn doPackages(initial, mode, false, opts)\n}\n\n// AllPackages creates an IR program for a set of packages plus all\n// their dependencies.\n//\n// The packages must have been loaded from source syntax using the\n// golang.org/x/tools/go/packages.Load function in LoadAllSyntax mode.\n//\n// AllPackages creates an IR package for each well-typed package in the\n// initial list, plus all their dependencies. The resulting list of\n// packages corresponds to the list of initial packages, and may contain\n// a nil if IR code could not be constructed for the corresponding\n// initial package due to type errors.\n//\n// Code for bodies of functions is not built until Build is called on\n// the resulting Program. IR code is constructed for all packages with\n// well-typed syntax trees.\n//\n// The mode parameter controls diagnostics and checking during IR construction.\nfunc AllPackages(initial []*packages.Package, mode ir.BuilderMode, opts *Options) (*ir.Program, []*ir.Package) {\n\treturn doPackages(initial, mode, true, opts)\n}\n\nfunc doPackages(initial []*packages.Package, mode ir.BuilderMode, deps bool, opts *Options) (*ir.Program, []*ir.Package) {\n\n\tvar fset *token.FileSet\n\tif len(initial) > 0 {\n\t\tfset = initial[0].Fset\n\t}\n\n\tprog := ir.NewProgram(fset, mode)\n\tif opts != nil {\n\t\tprog.PrintFunc = opts.PrintFunc\n\t}\n\n\tisInitial := make(map[*packages.Package]bool, len(initial))\n\tfor _, p := range initial {\n\t\tisInitial[p] = true\n\t}\n\n\tirmap := make(map[*packages.Package]*ir.Package)\n\tpackages.Visit(initial, nil, func(p *packages.Package) {\n\t\tif p.Types != nil && !p.IllTyped {\n\t\t\tvar files []*ast.File\n\t\t\tif deps || isInitial[p] {\n\t\t\t\tfiles = p.Syntax\n\t\t\t}\n\t\t\tirmap[p] = prog.CreatePackage(p.Types, files, p.TypesInfo, true)\n\t\t}\n\t})\n\n\tvar irpkgs []*ir.Package\n\tfor _, p := range initial {\n\t\tirpkgs = append(irpkgs, irmap[p]) // may be nil\n\t}\n\treturn prog, irpkgs\n}\n\n// CreateProgram returns a new program in IR form, given a program\n// loaded from source.  An IR package is created for each transitively\n// error-free package of lprog.\n//\n// Code for bodies of functions is not built until Build is called\n// on the result.\n//\n// The mode parameter controls diagnostics and checking during IR construction.\n//\n// Deprecated: use golang.org/x/tools/go/packages and the Packages\n// function instead; see ir.ExampleLoadPackages.\nfunc CreateProgram(lprog *loader.Program, mode ir.BuilderMode) *ir.Program {\n\tprog := ir.NewProgram(lprog.Fset, mode)\n\n\tfor _, info := range lprog.AllPackages {\n\t\tif info.TransitivelyErrorFree {\n\t\t\tprog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)\n\t\t}\n\t}\n\n\treturn prog\n}\n\n// BuildPackage builds an IR program with IR for a single package.\n//\n// It populates pkg by type-checking the specified file ASTs.  All\n// dependencies are loaded using the importer specified by tc, which\n// typically loads compiler export data; IR code cannot be built for\n// those packages.  BuildPackage then constructs an ir.Program with all\n// dependency packages created, and builds and returns the IR package\n// corresponding to pkg.\n//\n// The caller must have set pkg.Path() to the import path.\n//\n// The operation fails if there were any type-checking or import errors.\n//\n// See ../ir/example_test.go for an example.\nfunc BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ir.BuilderMode) (*ir.Package, *types.Info, error) {\n\tif fset == nil {\n\t\tpanic(\"no token.FileSet\")\n\t}\n\tif pkg.Path() == \"\" {\n\t\tpanic(\"package has no import path\")\n\t}\n\n\tinfo := &types.Info{\n\t\tTypes:        make(map[ast.Expr]types.TypeAndValue),\n\t\tDefs:         make(map[*ast.Ident]types.Object),\n\t\tUses:         make(map[*ast.Ident]types.Object),\n\t\tImplicits:    make(map[ast.Node]types.Object),\n\t\tScopes:       make(map[ast.Node]*types.Scope),\n\t\tSelections:   make(map[*ast.SelectorExpr]*types.Selection),\n\t\tInstances:    make(map[*ast.Ident]types.Instance),\n\t\tFileVersions: make(map[*ast.File]string),\n\t}\n\tif err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tprog := ir.NewProgram(fset, mode)\n\n\t// Create IR packages for all imports.\n\t// Order is not significant.\n\tcreated := make(map[*types.Package]bool)\n\tvar createAll func(pkgs []*types.Package)\n\tcreateAll = func(pkgs []*types.Package) {\n\t\tfor _, p := range pkgs {\n\t\t\tif !created[p] {\n\t\t\t\tcreated[p] = true\n\t\t\t\tprog.CreatePackage(p, nil, nil, true)\n\t\t\t\tcreateAll(p.Imports())\n\t\t\t}\n\t\t}\n\t}\n\tcreateAll(pkg.Imports())\n\n\t// Create and build the primary package.\n\tirpkg := prog.CreatePackage(pkg, files, info, false)\n\tirpkg.Build()\n\treturn irpkg, info, nil\n}\n"
  },
  {
    "path": "go/ir/irutil/load_test.go",
    "content": "// Copyright 2015 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage irutil_test\n\nimport (\n\t\"bytes\"\n\t\"go/ast\"\n\t\"go/importer\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\n\t\"golang.org/x/tools/go/packages\"\n)\n\nconst hello = `package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello, world\")\n}\n`\n\nfunc TestBuildPackage(t *testing.T) {\n\t// There is a more substantial test of BuildPackage and the\n\t// IR program it builds in ../ir/builder_test.go.\n\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"hello.go\", hello, parser.SkipObjectResolution)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tpkg := types.NewPackage(\"hello\", \"\")\n\tirpkg, _, err := irutil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, pkg, []*ast.File{f}, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif pkg.Name() != \"main\" {\n\t\tt.Errorf(\"pkg.Name() = %s, want main\", pkg.Name())\n\t}\n\tif irpkg.Func(\"main\") == nil {\n\t\tirpkg.WriteTo(os.Stderr)\n\t\tt.Errorf(\"irpkg has no main function\")\n\t}\n}\n\nfunc TestPackages(t *testing.T) {\n\tcfg := &packages.Config{Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo}\n\tinitial, err := packages.Load(cfg, \"bytes\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif packages.PrintErrors(initial) > 0 {\n\t\tt.Fatal(\"there were errors\")\n\t}\n\n\tprog, pkgs := irutil.Packages(initial, 0, nil)\n\tbytesNewBuffer := pkgs[0].Func(\"NewBuffer\")\n\tbytesNewBuffer.Pkg.Build()\n\n\t// We'll dump the IR of bytes.NewBuffer because it is small and stable.\n\tout := new(bytes.Buffer)\n\tbytesNewBuffer.WriteTo(out)\n\n\t// For determinism, sanitize the location.\n\tlocation := prog.Fset.Position(bytesNewBuffer.Pos()).String()\n\tgot := strings.Replace(out.String(), location, \"$GOROOT/src/bytes/buffer.go:1\", -1)\n\n\twant := `\n# Name: bytes.NewBuffer\n# Package: bytes\n# Location: $GOROOT/src/bytes/buffer.go:1\nfunc NewBuffer(buf []byte) *Buffer:\nb0: # entry\n\tt1 = Const <int> {0}\n\tt2 = Const <readOp> {0}\n\tt3 = Parameter <[]byte> {buf}\n\tt4 = HeapAlloc <*Buffer> # complit\n\tt5 = CompositeValue <Buffer> [100] t3 t1 t2\n\tStore {bytes.Buffer} t4 t5\n\tJump → b1\n\nb1: ← b0 # exit\n\tReturn t4\n\n`[1:]\n\tif got != want {\n\t\tt.Errorf(\"bytes.NewBuffer IR = <<%s>>, want <<%s>>\", got, want)\n\t}\n}\n\nfunc TestBuildPackage_MissingImport(t *testing.T) {\n\tfset := token.NewFileSet()\n\tf, err := parser.ParseFile(fset, \"bad.go\", `package bad; import \"missing\"`, parser.SkipObjectResolution)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tpkg := types.NewPackage(\"bad\", \"\")\n\tirpkg, _, err := irutil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)\n\tif err == nil || irpkg != nil {\n\t\tt.Fatal(\"BuildPackage succeeded unexpectedly\")\n\t}\n}\n\nfunc TestIssue28106(t *testing.T) {\n\t// In go1.10, go/packages loads all packages from source, not\n\t// export data, but does not type check function bodies of\n\t// imported packages. This test ensures that we do not attempt\n\t// to run the IR builder on functions without type information.\n\tcfg := &packages.Config{Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo}\n\tpkgs, err := packages.Load(cfg, \"runtime\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tprog, _ := irutil.Packages(pkgs, 0, nil)\n\tprog.Build() // no crash\n}\n"
  },
  {
    "path": "go/ir/irutil/loops.go",
    "content": "package irutil\n\nimport \"honnef.co/go/tools/go/ir\"\n\ntype Loop struct{ *ir.BlockSet }\n\nfunc FindLoops(fn *ir.Function) []Loop {\n\tif fn.Blocks == nil {\n\t\treturn nil\n\t}\n\ttree := fn.DomPreorder()\n\tvar sets []Loop\n\tfor _, h := range tree {\n\t\tfor _, n := range h.Preds {\n\t\t\tif !h.Dominates(n) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// n is a back-edge to h\n\t\t\t// h is the loop header\n\t\t\tif n == h {\n\t\t\t\tset := Loop{ir.NewBlockSet(len(fn.Blocks))}\n\t\t\t\tset.Add(n)\n\t\t\t\tsets = append(sets, set)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tset := Loop{ir.NewBlockSet(len(fn.Blocks))}\n\t\t\tset.Add(h)\n\t\t\tset.Add(n)\n\t\t\tfor _, b := range allPredsBut(n, h, nil) {\n\t\t\t\tset.Add(b)\n\t\t\t}\n\t\t\tsets = append(sets, set)\n\t\t}\n\t}\n\treturn sets\n}\n\nfunc allPredsBut(b, but *ir.BasicBlock, list []*ir.BasicBlock) []*ir.BasicBlock {\nouter:\n\tfor _, pred := range b.Preds {\n\t\tif pred == but {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, p := range list {\n\t\t\t// TODO improve big-o complexity of this function\n\t\t\tif pred == p {\n\t\t\t\tcontinue outer\n\t\t\t}\n\t\t}\n\t\tlist = append(list, pred)\n\t\tlist = allPredsBut(pred, but, list)\n\t}\n\treturn list\n}\n"
  },
  {
    "path": "go/ir/irutil/stub.go",
    "content": "package irutil\n\nimport (\n\t\"honnef.co/go/tools/go/ir\"\n)\n\n// IsStub reports whether a function is a stub. A function is\n// considered a stub if it has no instructions or if all it does is\n// return a constant value.\nfunc IsStub(fn *ir.Function) bool {\n\tfor _, b := range fn.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tswitch instr.(type) {\n\t\t\tcase *ir.Const:\n\t\t\t\t// const naturally has no side-effects\n\t\t\tcase *ir.Panic:\n\t\t\t\t// panic is a stub if it only uses constants\n\t\t\tcase *ir.Return:\n\t\t\t\t// return is a stub if it only uses constants\n\t\t\tcase *ir.DebugRef:\n\t\t\tcase *ir.Jump:\n\t\t\t\t// if there are no disallowed instructions, then we're\n\t\t\t\t// only jumping to the exit block (or possibly\n\t\t\t\t// somewhere else that's stubby?)\n\t\t\tdefault:\n\t\t\t\t// all other instructions are assumed to do actual work\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "go/ir/irutil/switch.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage irutil\n\n// This file implements discovery of switch and type-switch constructs\n// from low-level control flow.\n//\n// Many techniques exist for compiling a high-level switch with\n// constant cases to efficient machine code.  The optimal choice will\n// depend on the data type, the specific case values, the code in the\n// body of each case, and the hardware.\n// Some examples:\n// - a lookup table (for a switch that maps constants to constants)\n// - a computed goto\n// - a binary tree\n// - a perfect hash\n// - a two-level switch (to partition constant strings by their first byte).\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/go/ir\"\n)\n\n// A ConstCase represents a single constant comparison.\n// It is part of a Switch.\ntype ConstCase struct {\n\tBlock *ir.BasicBlock // block performing the comparison\n\tBody  *ir.BasicBlock // body of the case\n\tValue *ir.Const      // case comparand\n}\n\n// A TypeCase represents a single type assertion.\n// It is part of a Switch.\ntype TypeCase struct {\n\tBlock   *ir.BasicBlock // block performing the type assert\n\tBody    *ir.BasicBlock // body of the case\n\tType    types.Type     // case type\n\tBinding ir.Value       // value bound by this case\n}\n\n// A Switch is a logical high-level control flow operation\n// (a multiway branch) discovered by analysis of a CFG containing\n// only if/else chains.  It is not part of the ir.Instruction set.\n//\n// One of ConstCases and TypeCases has length >= 2;\n// the other is nil.\n//\n// In a value switch, the list of cases may contain duplicate constants.\n// A type switch may contain duplicate types, or types assignable\n// to an interface type also in the list.\n// TODO(adonovan): eliminate such duplicates.\ntype Switch struct {\n\tStart      *ir.BasicBlock // block containing start of if/else chain\n\tX          ir.Value       // the switch operand\n\tConstCases []ConstCase    // ordered list of constant comparisons\n\tTypeCases  []TypeCase     // ordered list of type assertions\n\tDefault    *ir.BasicBlock // successor if all comparisons fail\n}\n\nfunc (sw *Switch) String() string {\n\t// We represent each block by the String() of its\n\t// first Instruction, e.g. \"print(42:int)\".\n\tvar buf bytes.Buffer\n\tif sw.ConstCases != nil {\n\t\tfmt.Fprintf(&buf, \"switch %s {\\n\", sw.X.Name())\n\t\tfor _, c := range sw.ConstCases {\n\t\t\tfmt.Fprintf(&buf, \"case %s: %s\\n\", c.Value.Name(), c.Body.Instrs[0])\n\t\t}\n\t} else {\n\t\tfmt.Fprintf(&buf, \"switch %s.(type) {\\n\", sw.X.Name())\n\t\tfor _, c := range sw.TypeCases {\n\t\t\tfmt.Fprintf(&buf, \"case %s %s: %s\\n\",\n\t\t\t\tc.Binding.Name(), c.Type, c.Body.Instrs[0])\n\t\t}\n\t}\n\tif sw.Default != nil {\n\t\tfmt.Fprintf(&buf, \"default: %s\\n\", sw.Default.Instrs[0])\n\t}\n\tfmt.Fprintf(&buf, \"}\")\n\treturn buf.String()\n}\n\n// Switches examines the control-flow graph of fn and returns the\n// set of inferred value and type switches.  A value switch tests an\n// ir.Value for equality against two or more compile-time constant\n// values.  Switches involving link-time constants (addresses) are\n// ignored.  A type switch type-asserts an ir.Value against two or\n// more types.\n//\n// The switches are returned in dominance order.\n//\n// The resulting switches do not necessarily correspond to uses of the\n// 'switch' keyword in the source: for example, a single source-level\n// switch statement with non-constant cases may result in zero, one or\n// many Switches, one per plural sequence of constant cases.\n// Switches may even be inferred from if/else- or goto-based control flow.\n// (In general, the control flow constructs of the source program\n// cannot be faithfully reproduced from the IR.)\nfunc Switches(fn *ir.Function) []Switch {\n\t// Traverse the CFG in dominance order, so we don't\n\t// enter an if/else-chain in the middle.\n\tvar switches []Switch\n\tseen := make(map[*ir.BasicBlock]bool) // TODO(adonovan): opt: use ir.blockSet\n\tfor _, b := range fn.DomPreorder() {\n\t\tif x, k := isComparisonBlock(b); x != nil {\n\t\t\t// Block b starts a switch.\n\t\t\tsw := Switch{Start: b, X: x}\n\t\t\tvalueSwitch(&sw, k, seen)\n\t\t\tif len(sw.ConstCases) > 1 {\n\t\t\t\tswitches = append(switches, sw)\n\t\t\t}\n\t\t}\n\n\t\tif y, x, T := isTypeAssertBlock(b); y != nil {\n\t\t\t// Block b starts a type switch.\n\t\t\tsw := Switch{Start: b, X: x}\n\t\t\ttypeSwitch(&sw, y, T, seen)\n\t\t\tif len(sw.TypeCases) > 1 {\n\t\t\t\tswitches = append(switches, sw)\n\t\t\t}\n\t\t}\n\t}\n\treturn switches\n}\n\nfunc isSameX(x1 ir.Value, x2 ir.Value) bool {\n\tif x1 == x2 {\n\t\treturn true\n\t}\n\tif x2, ok := x2.(*ir.Sigma); ok {\n\t\treturn isSameX(x1, x2.X)\n\t}\n\treturn false\n}\n\nfunc valueSwitch(sw *Switch, k *ir.Const, seen map[*ir.BasicBlock]bool) {\n\tb := sw.Start\n\tx := sw.X\n\tfor isSameX(sw.X, x) {\n\t\tif seen[b] {\n\t\t\tbreak\n\t\t}\n\t\tseen[b] = true\n\n\t\tsw.ConstCases = append(sw.ConstCases, ConstCase{\n\t\t\tBlock: b,\n\t\t\tBody:  b.Succs[0],\n\t\t\tValue: k,\n\t\t})\n\t\tb = b.Succs[1]\n\t\tn := 0\n\t\tfor _, instr := range b.Instrs {\n\t\t\tswitch instr.(type) {\n\t\t\tcase *ir.If, *ir.BinOp:\n\t\t\t\tn++\n\t\t\tcase *ir.Sigma, *ir.Phi, *ir.DebugRef:\n\t\t\tdefault:\n\t\t\t\tn += 1000\n\t\t\t}\n\t\t}\n\t\tif n != 2 {\n\t\t\t// Block b contains not just 'if x == k' and σ/ϕ nodes,\n\t\t\t// so it may have side effects that\n\t\t\t// make it unsafe to elide.\n\t\t\tbreak\n\t\t}\n\t\tif len(b.Preds) != 1 {\n\t\t\t// Block b has multiple predecessors,\n\t\t\t// so it cannot be treated as a case.\n\t\t\tbreak\n\t\t}\n\t\tx, k = isComparisonBlock(b)\n\t}\n\tsw.Default = b\n}\n\nfunc typeSwitch(sw *Switch, y ir.Value, T types.Type, seen map[*ir.BasicBlock]bool) {\n\tb := sw.Start\n\tx := sw.X\n\tfor isSameX(sw.X, x) {\n\t\tif seen[b] {\n\t\t\tbreak\n\t\t}\n\t\tseen[b] = true\n\n\t\tsw.TypeCases = append(sw.TypeCases, TypeCase{\n\t\t\tBlock:   b,\n\t\t\tBody:    b.Succs[0],\n\t\t\tType:    T,\n\t\t\tBinding: y,\n\t\t})\n\t\tb = b.Succs[1]\n\t\tn := 0\n\t\tfor _, instr := range b.Instrs {\n\t\t\tswitch instr.(type) {\n\t\t\tcase *ir.TypeAssert, *ir.Extract, *ir.If:\n\t\t\t\tn++\n\t\t\tcase *ir.Sigma, *ir.Phi:\n\t\t\tdefault:\n\t\t\t\tn += 1000\n\t\t\t}\n\t\t}\n\t\tif n != 4 {\n\t\t\t// Block b contains not just\n\t\t\t//  {TypeAssert; Extract #0; Extract #1; If}\n\t\t\t// so it may have side effects that\n\t\t\t// make it unsafe to elide.\n\t\t\tbreak\n\t\t}\n\t\tif len(b.Preds) != 1 {\n\t\t\t// Block b has multiple predecessors,\n\t\t\t// so it cannot be treated as a case.\n\t\t\tbreak\n\t\t}\n\t\ty, x, T = isTypeAssertBlock(b)\n\t}\n\tsw.Default = b\n}\n\n// isComparisonBlock returns the operands (v, k) if a block ends with\n// a comparison v==k, where k is a compile-time constant.\nfunc isComparisonBlock(b *ir.BasicBlock) (v ir.Value, k *ir.Const) {\n\tif n := len(b.Instrs); n >= 2 {\n\t\tif i, ok := b.Instrs[n-1].(*ir.If); ok {\n\t\t\tif binop, ok := i.Cond.(*ir.BinOp); ok && binop.Block() == b && binop.Op == token.EQL {\n\t\t\t\tif k, ok := binop.Y.(*ir.Const); ok {\n\t\t\t\t\treturn binop.X, k\n\t\t\t\t}\n\t\t\t\tif k, ok := binop.X.(*ir.Const); ok {\n\t\t\t\t\treturn binop.Y, k\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// isTypeAssertBlock returns the operands (y, x, T) if a block ends with\n// a type assertion \"if y, ok := x.(T); ok {\".\nfunc isTypeAssertBlock(b *ir.BasicBlock) (y, x ir.Value, T types.Type) {\n\tif n := len(b.Instrs); n >= 4 {\n\t\tif i, ok := b.Instrs[n-1].(*ir.If); ok {\n\t\t\tif ext1, ok := i.Cond.(*ir.Extract); ok && ext1.Block() == b && ext1.Index == 1 {\n\t\t\t\tif ta, ok := ext1.Tuple.(*ir.TypeAssert); ok && ta.Block() == b {\n\t\t\t\t\t// hack: relies upon instruction ordering.\n\t\t\t\t\tif ext0, ok := b.Instrs[n-3].(*ir.Extract); ok {\n\t\t\t\t\t\treturn ext0, ta.X, ta.AssertedType\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "go/ir/irutil/switch_test.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// No testdata on Android.\n\n//go:build !android\n\npackage irutil\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/parser\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"honnef.co/go/tools/go/ir\"\n\n\t\"golang.org/x/tools/go/analysis/analysistest\"\n\t//lint:ignore SA1019 go/loader is deprecated, but works fine for our tests\n\t\"golang.org/x/tools/go/loader\"\n)\n\nfunc TestSwitches(t *testing.T) {\n\tconf := loader.Config{ParserMode: parser.ParseComments}\n\tf, err := conf.ParseFile(filepath.Join(analysistest.TestData(), \"switches.go\"), nil)\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\n\tconf.CreateFromFiles(\"main\", f)\n\tiprog, err := conf.Load()\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\n\tprog := CreateProgram(iprog, 0)\n\tmainPkg := prog.Package(iprog.Created[0].Pkg)\n\tmainPkg.Build()\n\n\tfor _, mem := range mainPkg.Members {\n\t\tif fn, ok := mem.(*ir.Function); ok {\n\t\t\tif fn.Synthetic != 0 {\n\t\t\t\tcontinue // e.g. init()\n\t\t\t}\n\t\t\t// Each (multi-line) \"switch\" comment within\n\t\t\t// this function must match the printed form\n\t\t\t// of a ConstSwitch.\n\t\t\tvar wantSwitches []string\n\t\t\tfor _, c := range f.Comments {\n\t\t\t\tif fn.Source().Pos() <= c.Pos() && c.Pos() < fn.Source().End() {\n\t\t\t\t\ttext := strings.TrimSpace(c.Text())\n\t\t\t\t\tif strings.HasPrefix(text, \"switch \") {\n\t\t\t\t\t\twantSwitches = append(wantSwitches, text)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitches := Switches(fn)\n\t\t\tif len(switches) != len(wantSwitches) {\n\t\t\t\tt.Errorf(\"in %s, found %d switches, want %d\", fn, len(switches), len(wantSwitches))\n\t\t\t}\n\t\t\tfor i, sw := range switches {\n\t\t\t\tgot := sw.testString()\n\t\t\t\tif i >= len(wantSwitches) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\twant := wantSwitches[i]\n\t\t\t\tif got != want {\n\t\t\t\t\tt.Errorf(\"in %s, found switch %d: got <<%s>>, want <<%s>>\", fn, i, got, want)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (sw *Switch) testString() string {\n\t// same as the actual String method, but use the second to last\n\t// instruction instead, to skip over all the phi and sigma nodes\n\t// that SSI produces.\n\tvar buf bytes.Buffer\n\tif sw.ConstCases != nil {\n\t\tfmt.Fprintf(&buf, \"switch %s {\\n\", sw.X.Name())\n\t\tfor _, c := range sw.ConstCases {\n\t\t\tn := max(len(c.Body.Instrs)-2, 0)\n\t\t\tfmt.Fprintf(&buf, \"case %s: %s\\n\", c.Value.Name(), c.Body.Instrs[n])\n\t\t}\n\t} else {\n\t\tfmt.Fprintf(&buf, \"switch %s.(type) {\\n\", sw.X.Name())\n\t\tfor _, c := range sw.TypeCases {\n\t\t\tn := max(len(c.Body.Instrs)-2, 0)\n\t\t\tfmt.Fprintf(&buf, \"case %s %s: %s\\n\",\n\t\t\t\tc.Binding.Name(), c.Type, c.Body.Instrs[n])\n\t\t}\n\t}\n\tif sw.Default != nil {\n\t\tn := max(len(sw.Default.Instrs)-2, 0)\n\t\tfmt.Fprintf(&buf, \"default: %s\\n\", sw.Default.Instrs[n])\n\t}\n\tfmt.Fprintf(&buf, \"}\")\n\treturn buf.String()\n}\n"
  },
  {
    "path": "go/ir/irutil/terminates.go",
    "content": "package irutil\n\nimport (\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/go/ir\"\n)\n\n// Terminates reports whether fn is supposed to return, that is if it\n// has at least one theoretic path that returns from the function.\n// Explicit panics do not count as terminating.\nfunc Terminates(fn *ir.Function) bool {\n\tif fn.Blocks == nil {\n\t\t// assuming that a function terminates is the conservative\n\t\t// choice\n\t\treturn true\n\t}\n\n\tfor _, block := range fn.Blocks {\n\t\tif _, ok := block.Control().(*ir.Return); ok {\n\t\t\tif len(block.Preds) == 0 {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tfor _, pred := range block.Preds {\n\t\t\t\tswitch ctrl := pred.Control().(type) {\n\t\t\t\tcase *ir.Panic:\n\t\t\t\t\t// explicit panics do not count as terminating\n\t\t\t\tcase *ir.If:\n\t\t\t\t\t// Check if we got here by receiving from a closed\n\t\t\t\t\t// time.Tick channel – this cannot happen at\n\t\t\t\t\t// runtime and thus doesn't constitute termination\n\t\t\t\t\tiff := ctrl\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\tex, ok := iff.Cond.(*ir.Extract)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\tif ex.Index != 1 {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\trecv, ok := ex.Tuple.(*ir.Recv)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\tcall, ok := recv.Chan.(*ir.Call)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\tfn, ok := call.Common().Value.(*ir.Function)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\tfn2, ok := fn.Object().(*types.Func)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\tif fn2.FullName() != \"time.Tick\" {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\t// we've reached the exit block\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "go/ir/irutil/testdata/switches.go",
    "content": "//go:build ignore\n// +build ignore\n\npackage main\n\n// This file is the input to TestSwitches in switch_test.go.\n// Each multiway conditional with constant or type cases (Switch)\n// discovered by Switches is printed, and compared with the\n// comments.\n//\n// The body of each case is printed as the value of its first\n// instruction.\n\n// -------- Value switches --------\n\nfunc four() int { return 4 }\n\n// A non-constant case makes a switch \"impure\", but its pure\n// cases form two separate switches.\nfunc SwitchWithNonConstantCase(x int) {\n\t// switch t12 {\n\t// case t1: Call <()> print t2\n\t// case t3: Call <()> print t5\n\t// case t4: Call <()> print t5\n\t// default: BinOp <bool> {==} t30 t31\n\t// }\n\n\t// switch t36 {\n\t// case t7: Call <()> print t8\n\t// case t9: Call <()> print t10\n\t// default: Call <()> print t11\n\t// }\n\tswitch x {\n\tcase 1:\n\t\tprint(1)\n\tcase 2, 3:\n\t\tprint(23)\n\tcase four():\n\t\tprint(3)\n\tcase 5:\n\t\tprint(5)\n\tcase 6:\n\t\tprint(6)\n\t}\n\tprint(\"done\")\n}\n\n// Switches may be found even where the source\n// program doesn't have a switch statement.\n\nfunc ImplicitSwitches(x, y int) {\n\t// switch t13 {\n\t// case t1: Call <()> print t4\n\t// case t2: Call <()> print t4\n\t// default: BinOp <bool> {<} t28 t3\n\t// }\n\tif x == 1 || 2 == x || x < 5 {\n\t\tprint(12)\n\t}\n\n\t// switch t25 {\n\t// case t5: Call <()> print t7\n\t// case t6: Call <()> print t7\n\t// default: BinOp <bool> {==} t50 t51\n\t// }\n\tif x == 3 || 4 == x || x == y {\n\t\tprint(34)\n\t}\n\n\t// Not a switch: no consistent variable.\n\tif x == 5 || y == 6 {\n\t\tprint(56)\n\t}\n\n\t// Not a switch: only one constant comparison.\n\tif x == 7 || x == y {\n\t\tprint(78)\n\t}\n}\n\nfunc IfElseBasedSwitch(x int) {\n\t// switch t6 {\n\t// case t1: Call <()> print t2\n\t// case t3: Call <()> print t4\n\t// default: Call <()> print t5\n\t// }\n\tif x == 1 {\n\t\tprint(1)\n\t} else if x == 2 {\n\t\tprint(2)\n\t} else {\n\t\tprint(\"else\")\n\t}\n}\n\nfunc GotoBasedSwitch(x int) {\n\t// switch t6 {\n\t// case t1: Call <()> print t4\n\t// case t2: Call <()> print t5\n\t// default: Call <()> print t3\n\t// }\n\tif x == 1 {\n\t\tgoto L1\n\t}\n\tif x == 2 {\n\t\tgoto L2\n\t}\n\tprint(\"else\")\nL1:\n\tprint(1)\n\tgoto end\nL2:\n\tprint(2)\nend:\n}\n\nfunc SwitchInAForLoop(x, y int) {\n\t// switch t13 {\n\t// case t2: Call <()> print t3\n\t// case t4: Call <()> print t5\n\t// default: BinOp <bool> {==} t31 t30\n\t// }\nloop:\n\tfor {\n\t\tprint(\"head\")\n\t\tswitch x {\n\t\tcase 1:\n\t\t\tprint(1)\n\t\t\tbreak loop\n\t\tcase 2:\n\t\t\tprint(2)\n\t\t\tbreak loop\n\t\tcase y:\n\t\t\tprint(3)\n\t\t\tbreak loop\n\t\t}\n\t}\n}\n\n// This case is a switch in a for-loop, both constructed using goto.\n// As before, the default case points back to the block containing the\n// switch, but that's ok.\nfunc SwitchInAForLoopUsingGoto(x int) {\n\t// switch t10 {\n\t// case t2: Call <()> print t4\n\t// case t3: Call <()> print t5\n\t// default: BinOp <bool> {==} t10 t2\n\t// }\nloop:\n\tprint(\"head\")\n\tif x == 1 {\n\t\tgoto L1\n\t}\n\tif x == 2 {\n\t\tgoto L2\n\t}\n\tgoto loop\nL1:\n\tprint(1)\n\tgoto end\nL2:\n\tprint(2)\nend:\n}\n\nfunc UnstructuredSwitchInAForLoop(x int) {\n\t// switch t9 {\n\t// case t1: Call <()> print t2\n\t// case t3: BinOp <bool> {==} t9 t1\n\t// default: Call <()> print t4\n\t// }\n\tfor {\n\t\tif x == 1 {\n\t\t\tprint(1)\n\t\t\treturn\n\t\t}\n\t\tif x == 2 {\n\t\t\tcontinue\n\t\t}\n\t\tbreak\n\t}\n\tprint(\"end\")\n}\n\nfunc CaseWithMultiplePreds(x int) {\n\tfor {\n\t\tif x == 1 {\n\t\t\tprint(1)\n\t\t\treturn\n\t\t}\n\tloop:\n\t\t// This block has multiple predecessors,\n\t\t// so can't be treated as a switch case.\n\t\tif x == 2 {\n\t\t\tgoto loop\n\t\t}\n\t\tbreak\n\t}\n\tprint(\"end\")\n}\n\nfunc DuplicateConstantsAreNotEliminated(x int) {\n\t// switch t7 {\n\t// case t1: Call <()> print t2\n\t// case t3: Call <()> print t4\n\t// case t5: Call <()> print t6\n\t// default: Return\n\t// }\n\tif x == 1 {\n\t\tprint(1)\n\t} else if x == 1 { // duplicate => unreachable\n\t\tprint(\"1a\")\n\t} else if x == 2 {\n\t\tprint(2)\n\t}\n}\n\n// Interface values (created by comparisons) are not constants,\n// so ConstSwitch.X is never of interface type.\nfunc MakeInterfaceIsNotAConstant(x interface{}) {\n\tif x == \"foo\" {\n\t\tprint(\"foo\")\n\t} else if x == 1 {\n\t\tprint(1)\n\t}\n}\n\nfunc ZeroInitializedVarsAreConstants(x int) {\n\t// switch t6 {\n\t// case t5: Call <()> print t1\n\t// case t2: Call <()> print t3\n\t// default: Call <()> print t4\n\t// }\n\tvar zero int // SSA construction replaces zero with 0\n\tif x == zero {\n\t\tprint(1)\n\t} else if x == 2 {\n\t\tprint(2)\n\t}\n\tprint(\"end\")\n}\n\n// -------- Type switches --------\n\n// NB, potentially fragile reliance on register number.\nfunc AdHocTypeSwitch(x interface{}) {\n\t// switch t2.(type) {\n\t// case t4 int: Call <()> println t8\n\t// case t13 string: Call <()> println t16\n\t// default: Call <()> print t1\n\t// }\n\tif i, ok := x.(int); ok {\n\t\tprintln(i)\n\t} else if s, ok := x.(string); ok {\n\t\tprintln(s)\n\t} else {\n\t\tprint(\"default\")\n\t}\n}\n"
  },
  {
    "path": "go/ir/irutil/util.go",
    "content": "package irutil\n\nimport (\n\t\"go/types\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n)\n\nfunc Reachable(from, to *ir.BasicBlock) bool {\n\tif from == to {\n\t\treturn true\n\t}\n\tif from.Dominates(to) {\n\t\treturn true\n\t}\n\n\tfound := false\n\tWalk(from, func(b *ir.BasicBlock) bool {\n\t\tif b == to {\n\t\t\tfound = true\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t})\n\treturn found\n}\n\nfunc Walk(b *ir.BasicBlock, fn func(*ir.BasicBlock) bool) {\n\tseen := map[*ir.BasicBlock]bool{}\n\twl := []*ir.BasicBlock{b}\n\tfor len(wl) > 0 {\n\t\tb := wl[len(wl)-1]\n\t\twl = wl[:len(wl)-1]\n\t\tif seen[b] {\n\t\t\tcontinue\n\t\t}\n\t\tseen[b] = true\n\t\tif !fn(b) {\n\t\t\tcontinue\n\t\t}\n\t\twl = append(wl, b.Succs...)\n\t}\n}\n\nfunc Vararg(x *ir.Slice) ([]ir.Value, bool) {\n\tvar out []ir.Value\n\talloc, ok := ir.Unwrap(x.X).(*ir.Alloc)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tvar checkAlloc func(alloc ir.Value) bool\n\tcheckAlloc = func(alloc ir.Value) bool {\n\t\tfor _, ref := range *alloc.Referrers() {\n\t\t\tif ref == x {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif ref.Block() != x.Block() {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tswitch ref := ref.(type) {\n\t\t\tcase *ir.IndexAddr:\n\t\t\t\tidx := ref\n\t\t\t\tif len(*idx.Referrers()) != 1 {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tstore, ok := (*idx.Referrers())[0].(*ir.Store)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tout = append(out, store.Val)\n\t\t\tcase *ir.Copy:\n\t\t\t\tif !checkAlloc(ref) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\tif !checkAlloc(alloc) {\n\t\treturn nil, false\n\t}\n\treturn out, true\n}\n\nfunc CallName(call *ir.CallCommon) string {\n\tif call.IsInvoke() {\n\t\treturn \"\"\n\t}\n\tswitch v := call.Value.(type) {\n\tcase *ir.Function:\n\t\tfn, ok := v.Object().(*types.Func)\n\t\tif !ok {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn typeutil.FuncName(fn)\n\tcase *ir.Builtin:\n\t\treturn v.Name()\n\t}\n\treturn \"\"\n}\n\nfunc IsCallTo(call *ir.CallCommon, name string) bool { return CallName(call) == name }\n\nfunc IsCallToAny(call *ir.CallCommon, names ...string) bool {\n\tq := CallName(call)\n\treturn slices.Contains(names, q)\n}\n\nfunc FilterDebug(instr []ir.Instruction) []ir.Instruction {\n\tvar out []ir.Instruction\n\tfor _, ins := range instr {\n\t\tif _, ok := ins.(*ir.DebugRef); !ok {\n\t\t\tout = append(out, ins)\n\t\t}\n\t}\n\treturn out\n}\n\nfunc IsExample(fn *ir.Function) bool {\n\tif !strings.HasPrefix(fn.Name(), \"Example\") {\n\t\treturn false\n\t}\n\tf := fn.Prog.Fset.File(fn.Pos())\n\tif f == nil {\n\t\treturn false\n\t}\n\treturn strings.HasSuffix(f.Name(), \"_test.go\")\n}\n\n// Flatten recursively returns the underlying value of an ir.Sigma or\n// ir.Phi node. If all edges in an ir.Phi node are the same (after\n// flattening), the flattened edge will get returned. If flattening is\n// not possible, nil is returned.\nfunc Flatten(v ir.Value) ir.Value {\n\tfailed := false\n\tseen := map[ir.Value]struct{}{}\n\tvar out ir.Value\n\tvar dfs func(v ir.Value)\n\tdfs = func(v ir.Value) {\n\t\tif failed {\n\t\t\treturn\n\t\t}\n\t\tif _, ok := seen[v]; ok {\n\t\t\treturn\n\t\t}\n\t\tseen[v] = struct{}{}\n\n\t\tswitch v := v.(type) {\n\t\tcase *ir.Sigma:\n\t\t\tdfs(v.X)\n\t\tcase *ir.Phi:\n\t\t\tfor _, e := range v.Edges {\n\t\t\t\tdfs(e)\n\t\t\t}\n\t\tdefault:\n\t\t\tif out == nil {\n\t\t\t\tout = v\n\t\t\t} else if out != v {\n\t\t\t\tfailed = true\n\t\t\t}\n\t\t}\n\t}\n\tdfs(v)\n\n\tif failed {\n\t\treturn nil\n\t}\n\treturn out\n}\n"
  },
  {
    "path": "go/ir/irutil/visit.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage irutil\n\nimport \"honnef.co/go/tools/go/ir\"\n\n// This file defines utilities for visiting the IR of\n// a Program.\n//\n// TODO(adonovan): test coverage.\n\n// AllFunctions finds and returns the set of functions potentially\n// needed by program prog, as determined by a simple linker-style\n// reachability algorithm starting from the members and method-sets of\n// each package.  The result may include anonymous functions and\n// synthetic wrappers.\n//\n// Precondition: all packages are built.\nfunc AllFunctions(prog *ir.Program) map[*ir.Function]bool {\n\tvisit := visitor{\n\t\tprog: prog,\n\t\tseen: make(map[*ir.Function]bool),\n\t}\n\tvisit.program()\n\treturn visit.seen\n}\n\ntype visitor struct {\n\tprog *ir.Program\n\tseen map[*ir.Function]bool\n}\n\nfunc (visit *visitor) program() {\n\tfor _, pkg := range visit.prog.AllPackages() {\n\t\tfor _, mem := range pkg.Members {\n\t\t\tif fn, ok := mem.(*ir.Function); ok {\n\t\t\t\tvisit.function(fn)\n\t\t\t}\n\t\t}\n\t}\n\tfor _, T := range visit.prog.RuntimeTypes() {\n\t\tmset := visit.prog.MethodSets.MethodSet(T)\n\t\tfor i, n := 0, mset.Len(); i < n; i++ {\n\t\t\tvisit.function(visit.prog.MethodValue(mset.At(i)))\n\t\t}\n\t}\n}\n\nfunc (visit *visitor) function(fn *ir.Function) {\n\tif !visit.seen[fn] {\n\t\tvisit.seen[fn] = true\n\t\tvar buf [10]*ir.Value // avoid alloc in common case\n\t\tfor _, b := range fn.Blocks {\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\tfor _, op := range instr.Operands(buf[:0]) {\n\t\t\t\t\tif fn, ok := (*op).(*ir.Function); ok {\n\t\t\t\t\t\tvisit.function(fn)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// MainPackages returns the subset of the specified packages\n// named \"main\" that define a main function.\n// The result may include synthetic \"testmain\" packages.\nfunc MainPackages(pkgs []*ir.Package) []*ir.Package {\n\tvar mains []*ir.Package\n\tfor _, pkg := range pkgs {\n\t\tif pkg.Pkg.Name() == \"main\" && pkg.Func(\"main\") != nil {\n\t\t\tmains = append(mains, pkg)\n\t\t}\n\t}\n\treturn mains\n}\n"
  },
  {
    "path": "go/ir/lift.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file defines the lifting pass which tries to \"lift\" Alloc\n// cells (new/local variables) into SSA registers, replacing loads\n// with the dominating stored value, eliminating loads and stores, and\n// inserting φ- and σ-nodes as needed.\n\n// Cited papers and resources:\n//\n// Ron Cytron et al. 1991. Efficiently computing SSA form...\n// https://doi.acm.org/10.1145/115372.115320\n//\n// Cooper, Harvey, Kennedy.  2001.  A Simple, Fast Dominance Algorithm.\n// Software Practice and Experience 2001, 4:1-10.\n// https://www.hipersoft.rice.edu/grads/publications/dom14.pdf\n//\n// Daniel Berlin, llvmdev mailing list, 2012.\n// https://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html\n// (Be sure to expand the whole thread.)\n//\n// C. Scott Ananian. 1997. The static single information form.\n//\n// Jeremy Singer. 2006. Static program analysis based on virtual register renaming.\n\n// TODO(adonovan): opt: there are many optimizations worth evaluating, and\n// the conventional wisdom for SSA construction is that a simple\n// algorithm well engineered often beats those of better asymptotic\n// complexity on all but the most egregious inputs.\n//\n// Danny Berlin suggests that the Cooper et al. algorithm for\n// computing the dominance frontier is superior to Cytron et al.\n// Furthermore he recommends that rather than computing the DF for the\n// whole function then renaming all alloc cells, it may be cheaper to\n// compute the DF for each alloc cell separately and throw it away.\n//\n// Consider exploiting liveness information to avoid creating dead\n// φ-nodes which we then immediately remove.\n//\n// Also see many other \"TODO: opt\" suggestions in the code.\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"os\"\n\t\"slices\"\n)\n\n// If true, show diagnostic information at each step of lifting.\n// Very verbose.\nconst debugLifting = false\n\n// domFrontier maps each block to the set of blocks in its dominance\n// frontier.  The outer slice is conceptually a map keyed by\n// Block.Index.  The inner slice is conceptually a set, possibly\n// containing duplicates.\n//\n// TODO(adonovan): opt: measure impact of dups; consider a packed bit\n// representation, e.g. big.Int, and bitwise parallel operations for\n// the union step in the Children loop.\n//\n// domFrontier's methods mutate the slice's elements but not its\n// length, so their receivers needn't be pointers.\ntype domFrontier BlockMap[[]*BasicBlock]\n\nfunc (df domFrontier) add(u, v *BasicBlock) {\n\tdf[u.Index] = append(df[u.Index], v)\n}\n\n// build builds the dominance frontier df for the dominator tree of\n// fn, using the algorithm found in A Simple, Fast Dominance\n// Algorithm, Figure 5.\n//\n// TODO(adonovan): opt: consider Berlin approach, computing pruned SSA\n// by pruning the entire IDF computation, rather than merely pruning\n// the DF -> IDF step.\nfunc (df domFrontier) build(fn *Function) {\n\tfor _, b := range fn.Blocks {\n\t\tpreds := b.Preds[0:len(b.Preds):len(b.Preds)]\n\t\tif b == fn.Exit {\n\t\t\tfor i, v := range fn.fakeExits.values {\n\t\t\t\tif v {\n\t\t\t\t\tpreds = append(preds, fn.Blocks[i])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif len(preds) >= 2 {\n\t\t\tfor _, p := range preds {\n\t\t\t\trunner := p\n\t\t\t\tfor runner != b.dom.idom {\n\t\t\t\t\tdf.add(runner, b)\n\t\t\t\t\trunner = runner.dom.idom\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc buildDomFrontier(fn *Function) domFrontier {\n\tdf := make(domFrontier, len(fn.Blocks))\n\tdf.build(fn)\n\treturn df\n}\n\ntype postDomFrontier BlockMap[[]*BasicBlock]\n\nfunc (rdf postDomFrontier) add(u, v *BasicBlock) {\n\trdf[u.Index] = append(rdf[u.Index], v)\n}\n\nfunc (rdf postDomFrontier) build(fn *Function) {\n\tfor _, b := range fn.Blocks {\n\t\tsuccs := b.Succs[0:len(b.Succs):len(b.Succs)]\n\t\tif fn.fakeExits.Has(b) {\n\t\t\tsuccs = append(succs, fn.Exit)\n\t\t}\n\t\tif len(succs) >= 2 {\n\t\t\tfor _, s := range succs {\n\t\t\t\trunner := s\n\t\t\t\tfor runner != b.pdom.idom {\n\t\t\t\t\trdf.add(runner, b)\n\t\t\t\t\trunner = runner.pdom.idom\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc buildPostDomFrontier(fn *Function) postDomFrontier {\n\trdf := make(postDomFrontier, len(fn.Blocks))\n\trdf.build(fn)\n\treturn rdf\n}\n\nfunc removeInstr(refs []Instruction, instr Instruction) []Instruction {\n\treturn removeInstrsIf(refs, func(i Instruction) bool { return i == instr })\n}\n\nfunc removeInstrsIf(refs []Instruction, p func(Instruction) bool) []Instruction {\n\treturn slices.DeleteFunc(refs, p)\n}\n\nfunc clearInstrs(instrs []Instruction) {\n\tfor i := range instrs {\n\t\tinstrs[i] = nil\n\t}\n}\n\nfunc numberNodesPerBlock(f *Function) {\n\tfor _, b := range f.Blocks {\n\t\tvar base ID\n\t\tfor _, instr := range b.Instrs {\n\t\t\tif instr == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tinstr.setID(base)\n\t\t\tbase++\n\t\t}\n\t}\n}\n\n// lift replaces local and new Allocs accessed only with\n// load/store by IR registers, inserting φ- and σ-nodes where necessary.\n// The result is a program in pruned SSI form.\n//\n// Preconditions:\n// - fn has no dead blocks (blockopt has run).\n// - Def/use info (Operands and Referrers) is up-to-date.\n// - The dominator tree is up-to-date.\nfunc lift(fn *Function) bool {\n\t// TODO(adonovan): opt: lots of little optimizations may be\n\t// worthwhile here, especially if they cause us to avoid\n\t// buildDomFrontier.  For example:\n\t//\n\t// - Alloc never loaded?  Eliminate.\n\t// - Alloc never stored?  Replace all loads with a zero constant.\n\t// - Alloc stored once?  Replace loads with dominating store;\n\t//   don't forget that an Alloc is itself an effective store\n\t//   of zero.\n\t// - Alloc used only within a single block?\n\t//   Use degenerate algorithm avoiding φ-nodes.\n\t// - Consider synergy with scalar replacement of aggregates (SRA).\n\t//   e.g. *(&x.f) where x is an Alloc.\n\t//   Perhaps we'd get better results if we generated this as x.f\n\t//   i.e. Field(x, .f) instead of Load(FieldIndex(x, .f)).\n\t//   Unclear.\n\t//\n\t// But we will start with the simplest correct code.\n\tvar df domFrontier\n\tvar rdf postDomFrontier\n\tvar closure *closure\n\tvar newPhis BlockMap[[]newPhi]\n\tvar newSigmas BlockMap[[]newSigma]\n\n\t// During this pass we will replace some BasicBlock.Instrs\n\t// (allocs, loads and stores) with nil, keeping a count in\n\t// BasicBlock.gaps.  At the end we will reset Instrs to the\n\t// concatenation of all non-dead newPhis and non-nil Instrs\n\t// for the block, reusing the original array if space permits.\n\n\t// While we're here, we also eliminate 'rundefers'\n\t// instructions and ssa:deferstack() in functions that contain no\n\t// 'defer' instructions. Eliminate ssa:deferstack() if it does not\n\t// escape.\n\tusesDefer := false\n\tdeferstackAlloc, deferstackCall := deferstackPreamble(fn)\n\teliminateDeferStack := deferstackAlloc != nil && !deferstackAlloc.Heap\n\n\t// Determine which allocs we can lift and number them densely.\n\t// The renaming phase uses this numbering for compact maps.\n\tnumAllocs := 0\n\n\tinstructions := make(BlockMap[liftInstructions], len(fn.Blocks))\n\tfor i := range instructions {\n\t\tinstructions[i].insertInstructions = map[Instruction][]Instruction{}\n\t}\n\n\t// Number nodes, for liftable\n\tnumberNodesPerBlock(fn)\n\n\tfor _, b := range fn.Blocks {\n\t\tb.gaps = 0\n\t\tb.rundefers = 0\n\n\t\tfor _, instr := range b.Instrs {\n\t\t\tswitch instr := instr.(type) {\n\t\t\tcase *Alloc:\n\t\t\t\tif !liftable(instr, instructions) {\n\t\t\t\t\tinstr.index = -1\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif numAllocs == 0 {\n\t\t\t\t\tdf = buildDomFrontier(fn)\n\t\t\t\t\trdf = buildPostDomFrontier(fn)\n\t\t\t\t\tif len(fn.Blocks) > 2 {\n\t\t\t\t\t\tclosure = transitiveClosure(fn)\n\t\t\t\t\t}\n\t\t\t\t\tnewPhis = make(BlockMap[[]newPhi], len(fn.Blocks))\n\t\t\t\t\tnewSigmas = make(BlockMap[[]newSigma], len(fn.Blocks))\n\n\t\t\t\t\tif debugLifting {\n\t\t\t\t\t\ttitle := false\n\t\t\t\t\t\tfor i, blocks := range df {\n\t\t\t\t\t\t\tif blocks != nil {\n\t\t\t\t\t\t\t\tif !title {\n\t\t\t\t\t\t\t\t\tfmt.Fprintf(os.Stderr, \"Dominance frontier of %s:\\n\", fn)\n\t\t\t\t\t\t\t\t\ttitle = true\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfmt.Fprintf(os.Stderr, \"\\t%s: %s\\n\", fn.Blocks[i], blocks)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tinstr.index = numAllocs\n\t\t\t\tnumAllocs++\n\t\t\tcase *Defer:\n\t\t\t\tusesDefer = true\n\t\t\t\tif eliminateDeferStack {\n\t\t\t\t\t// Clear _DeferStack and remove references to loads\n\t\t\t\t\tif instr._DeferStack != nil {\n\t\t\t\t\t\tif refs := instr._DeferStack.Referrers(); refs != nil {\n\t\t\t\t\t\t\t*refs = removeInstr(*refs, instr)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tinstr._DeferStack = nil\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase *RunDefers:\n\t\t\t\tb.rundefers++\n\t\t\t}\n\t\t}\n\t}\n\n\tif numAllocs > 0 {\n\t\tfor _, b := range fn.Blocks {\n\t\t\twork := instructions[b.Index]\n\t\t\tfor _, rename := range work.renameAllocs {\n\t\t\t\tfor _, instr_ := range b.Instrs[rename.startingAt:] {\n\t\t\t\t\treplace(instr_, rename.from, rename.to)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor _, b := range fn.Blocks {\n\t\t\twork := instructions[b.Index]\n\t\t\tif len(work.insertInstructions) != 0 {\n\t\t\t\tnewInstrs := make([]Instruction, 0, len(fn.Blocks)+len(work.insertInstructions)*3)\n\t\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\t\tif add, ok := work.insertInstructions[instr]; ok {\n\t\t\t\t\t\tnewInstrs = append(newInstrs, add...)\n\t\t\t\t\t}\n\t\t\t\t\tnewInstrs = append(newInstrs, instr)\n\t\t\t\t}\n\t\t\t\tb.Instrs = newInstrs\n\t\t\t}\n\t\t}\n\n\t\t// TODO(dh): remove inserted allocs that end up unused after lifting.\n\n\t\tfor _, b := range fn.Blocks {\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\tif instr, ok := instr.(*Alloc); ok && instr.index >= 0 {\n\t\t\t\t\tliftAlloc(closure, df, rdf, instr, newPhis, newSigmas)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// renaming maps an alloc (keyed by index) to its replacement\n\t\t// value.  Initially the renaming contains nil, signifying the\n\t\t// zero constant of the appropriate type; we construct the\n\t\t// Const lazily at most once on each path through the domtree.\n\t\t// TODO(adonovan): opt: cache per-function not per subtree.\n\t\trenaming := make([]Value, numAllocs)\n\n\t\t// Renaming.\n\t\trename(fn.Blocks[0], renaming, newPhis, newSigmas)\n\n\t\tsimplifyPhisAndSigmas(newPhis, newSigmas)\n\n\t\t// Eliminate dead φ- and σ-nodes.\n\t\tmarkLiveNodes(fn.Blocks, newPhis, newSigmas)\n\n\t\t// Eliminate ssa:deferstack() call.\n\t\tif eliminateDeferStack {\n\t\t\tb := deferstackCall.block\n\t\t\tfor i, instr := range b.Instrs {\n\t\t\t\tif instr == deferstackCall {\n\t\t\t\t\tb.Instrs[i] = nil\n\t\t\t\t\tb.gaps++\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Prepend remaining live φ-nodes to each block and possibly kill rundefers.\n\tfor _, b := range fn.Blocks {\n\t\tvar head []Instruction\n\t\tif numAllocs > 0 {\n\t\t\tnps := newPhis[b.Index]\n\t\t\thead = make([]Instruction, 0, len(nps))\n\t\t\tfor _, pred := range b.Preds {\n\t\t\t\tnss := newSigmas[pred.Index]\n\t\t\t\tidx := pred.succIndex(b)\n\t\t\t\tfor _, newSigma := range nss {\n\t\t\t\t\tif sigma := newSigma.sigmas[idx]; sigma != nil && sigma.live {\n\t\t\t\t\t\thead = append(head, sigma)\n\n\t\t\t\t\t\t// we didn't populate referrers before, as most\n\t\t\t\t\t\t// sigma nodes will be killed\n\t\t\t\t\t\tif refs := sigma.X.Referrers(); refs != nil {\n\t\t\t\t\t\t\t*refs = append(*refs, sigma)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if sigma != nil {\n\t\t\t\t\t\tsigma.block = nil\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor _, np := range nps {\n\t\t\t\tif np.phi.live {\n\t\t\t\t\thead = append(head, np.phi)\n\t\t\t\t} else {\n\t\t\t\t\tfor _, edge := range np.phi.Edges {\n\t\t\t\t\t\tif refs := edge.Referrers(); refs != nil {\n\t\t\t\t\t\t\t*refs = removeInstr(*refs, np.phi)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tnp.phi.block = nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trundefersToKill := b.rundefers\n\t\tif usesDefer {\n\t\t\trundefersToKill = 0\n\t\t}\n\n\t\tj := len(head)\n\t\tif j+b.gaps+rundefersToKill == 0 {\n\t\t\tcontinue // fast path: no new phis or gaps\n\t\t}\n\n\t\t// We could do straight copies instead of element-wise copies\n\t\t// when both b.gaps and rundefersToKill are zero. However,\n\t\t// that seems to only be the case ~1% of the time, which\n\t\t// doesn't seem worth the extra branch.\n\n\t\t// Remove dead instructions, add phis and sigmas\n\t\tns := len(b.Instrs) + j - b.gaps - rundefersToKill\n\t\tif ns <= cap(b.Instrs) {\n\t\t\t// b.Instrs has enough capacity to store all instructions\n\n\t\t\t// OPT(dh): check cap vs the actually required space; if\n\t\t\t// there is a big enough difference, it may be worth\n\t\t\t// allocating a new slice, to avoid pinning memory.\n\t\t\tdst := b.Instrs[:cap(b.Instrs)]\n\t\t\ti := len(dst) - 1\n\t\t\tfor n := len(b.Instrs) - 1; n >= 0; n-- {\n\t\t\t\tinstr := dst[n]\n\t\t\t\tif instr == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !usesDefer {\n\t\t\t\t\tif _, ok := instr.(*RunDefers); ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdst[i] = instr\n\t\t\t\ti--\n\t\t\t}\n\t\t\toff := i + 1 - len(head)\n\t\t\t// aid GC\n\t\t\tclearInstrs(dst[:off])\n\t\t\tdst = dst[off:]\n\t\t\tcopy(dst, head)\n\t\t\tb.Instrs = dst\n\t\t} else {\n\t\t\t// not enough space, so allocate a new slice and copy\n\t\t\t// over.\n\t\t\tdst := make([]Instruction, ns)\n\t\t\tcopy(dst, head)\n\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\tif instr == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !usesDefer {\n\t\t\t\t\tif _, ok := instr.(*RunDefers); ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdst[j] = instr\n\t\t\t\tj++\n\t\t\t}\n\t\t\tb.Instrs = dst\n\t\t}\n\t}\n\n\t// Remove any fn.Locals that were lifted.\n\tj := 0\n\tfor _, l := range fn.Locals {\n\t\tif l.index < 0 {\n\t\t\tfn.Locals[j] = l\n\t\t\tj++\n\t\t}\n\t}\n\t// Nil out fn.Locals[j:] to aid GC.\n\tfor i := j; i < len(fn.Locals); i++ {\n\t\tfn.Locals[i] = nil\n\t}\n\tfn.Locals = fn.Locals[:j]\n\n\treturn numAllocs > 0\n}\n\nfunc hasDirectReferrer(instr Instruction) bool {\n\tfor _, instr := range *instr.Referrers() {\n\t\tswitch instr.(type) {\n\t\tcase *Phi, *Sigma:\n\t\t\t// ignore\n\t\tdefault:\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc markLiveNodes(blocks []*BasicBlock, newPhis BlockMap[[]newPhi], newSigmas BlockMap[[]newSigma]) {\n\t// Phis and sigmas may become dead due to optimization passes. We may also insert more nodes than strictly\n\t// necessary, e.g. sigma nodes for constants, which will never be used.\n\n\t// Phi and sigma nodes are considered live if a non-phi, non-sigma\n\t// node uses them. Once we find a node that is live, we mark all\n\t// of its operands as used, too.\n\tfor _, npList := range newPhis {\n\t\tfor _, np := range npList {\n\t\t\tphi := np.phi\n\t\t\tif !phi.live && hasDirectReferrer(phi) {\n\t\t\t\tmarkLivePhi(phi)\n\t\t\t}\n\t\t}\n\t}\n\tfor _, npList := range newSigmas {\n\t\tfor _, np := range npList {\n\t\t\tfor _, sigma := range np.sigmas {\n\t\t\t\tif sigma != nil && !sigma.live && hasDirectReferrer(sigma) {\n\t\t\t\t\tmarkLiveSigma(sigma)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// Existing φ-nodes due to && and || operators\n\t// are all considered live (see Go issue 19622).\n\tfor _, b := range blocks {\n\t\tfor _, phi := range b.phis() {\n\t\t\tmarkLivePhi(phi.(*Phi))\n\t\t}\n\t}\n}\n\nfunc markLivePhi(phi *Phi) {\n\tphi.live = true\n\tfor _, rand := range phi.Edges {\n\t\tswitch rand := rand.(type) {\n\t\tcase *Phi:\n\t\t\tif !rand.live {\n\t\t\t\tmarkLivePhi(rand)\n\t\t\t}\n\t\tcase *Sigma:\n\t\t\tif !rand.live {\n\t\t\t\tmarkLiveSigma(rand)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc markLiveSigma(sigma *Sigma) {\n\tsigma.live = true\n\tswitch rand := sigma.X.(type) {\n\tcase *Phi:\n\t\tif !rand.live {\n\t\t\tmarkLivePhi(rand)\n\t\t}\n\tcase *Sigma:\n\t\tif !rand.live {\n\t\t\tmarkLiveSigma(rand)\n\t\t}\n\t}\n}\n\n// simplifyPhisAndSigmas removes duplicate phi and sigma nodes,\n// and replaces trivial phis with non-phi alternatives. Phi\n// nodes where all edges are identical, or consist of only the phi\n// itself and one other value, may be replaced with the value.\nfunc simplifyPhisAndSigmas(newPhis BlockMap[[]newPhi], newSigmas BlockMap[[]newSigma]) {\n\t// temporary numbering of values used in phis so that we can build map keys\n\tvar id ID\n\tfor _, npList := range newPhis {\n\t\tfor _, np := range npList {\n\t\t\tfor _, edge := range np.phi.Edges {\n\t\t\t\tedge.setID(id)\n\t\t\t\tid++\n\t\t\t}\n\t\t}\n\t}\n\t// find all phis that are trivial and can be replaced with a\n\t// non-phi value. run until we reach a fixpoint, because replacing\n\t// a phi may make other phis trivial.\n\tfor changed := true; changed; {\n\t\tchanged = false\n\t\tfor _, npList := range newPhis {\n\t\t\tfor _, np := range npList {\n\t\t\t\tif np.phi.live {\n\t\t\t\t\t// we're reusing 'live' to mean 'dead' in the context of simplifyPhisAndSigmas\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif r, ok := isUselessPhi(np.phi); ok {\n\t\t\t\t\t// useless phi, replace its uses with the\n\t\t\t\t\t// replacement value. the dead phi pass will clean\n\t\t\t\t\t// up the phi afterwards.\n\t\t\t\t\treplaceAll(np.phi, r)\n\t\t\t\t\tnp.phi.live = true\n\t\t\t\t\tchanged = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Replace duplicate sigma nodes with a single node. These nodes exist when multiple allocs get replaced with the\n\t\t// same dominating store.\n\t\tfor _, sigmaList := range newSigmas {\n\t\t\tprimarySigmas := map[struct {\n\t\t\t\tsucc int\n\t\t\t\tv    Value\n\t\t\t}]*Sigma{}\n\t\t\tfor _, sigmas := range sigmaList {\n\t\t\t\tfor succ, sigma := range sigmas.sigmas {\n\t\t\t\t\tif sigma == nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif sigma.live {\n\t\t\t\t\t\t// we're reusing 'live' to mean 'dead' in the context of simplifyPhisAndSigmas\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tkey := struct {\n\t\t\t\t\t\tsucc int\n\t\t\t\t\t\tv    Value\n\t\t\t\t\t}{succ, sigma.X}\n\t\t\t\t\tif alt, ok := primarySigmas[key]; ok {\n\t\t\t\t\t\treplaceAll(sigma, alt)\n\t\t\t\t\t\tsigma.live = true\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprimarySigmas[key] = sigma\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Replace duplicate phi nodes with a single node. As far as we know, these duplicate nodes only ever exist\n\t\t// because of the previous sigma deduplication.\n\t\tkeyb := make([]byte, 0, 4*8)\n\t\tfor _, npList := range newPhis {\n\t\t\tprimaryPhis := map[string]*Phi{}\n\t\t\tfor _, np := range npList {\n\t\t\t\tif np.phi.live {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif n := len(np.phi.Edges) * 8; cap(keyb) >= n {\n\t\t\t\t\tkeyb = keyb[:n]\n\t\t\t\t} else {\n\t\t\t\t\tkeyb = make([]byte, n, n*2)\n\t\t\t\t}\n\t\t\t\tfor i, e := range np.phi.Edges {\n\t\t\t\t\tbinary.LittleEndian.PutUint64(keyb[i*8:i*8+8], uint64(e.ID()))\n\t\t\t\t}\n\t\t\t\tif alt, ok := primaryPhis[string(keyb)]; ok {\n\t\t\t\t\treplaceAll(np.phi, alt)\n\t\t\t\t\tnp.phi.live = true\n\t\t\t\t\tchanged = true\n\t\t\t\t} else {\n\t\t\t\t\tprimaryPhis[string(keyb)] = np.phi\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\tfor _, npList := range newPhis {\n\t\tfor _, np := range npList {\n\t\t\tnp.phi.live = false\n\t\t\tfor _, edge := range np.phi.Edges {\n\t\t\t\tedge.setID(0)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, sigmaList := range newSigmas {\n\t\tfor _, sigmas := range sigmaList {\n\t\t\tfor _, sigma := range sigmas.sigmas {\n\t\t\t\tif sigma != nil {\n\t\t\t\t\tsigma.live = false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\ntype BlockSet struct {\n\tidx    int\n\tvalues []bool\n\tcount  int\n}\n\nfunc NewBlockSet(size int) *BlockSet {\n\treturn &BlockSet{values: make([]bool, size)}\n}\n\nfunc (s *BlockSet) Set(s2 *BlockSet) {\n\tcopy(s.values, s2.values)\n\ts.count = 0\n\tfor _, v := range s.values {\n\t\tif v {\n\t\t\ts.count++\n\t\t}\n\t}\n}\n\nfunc (s *BlockSet) Num() int {\n\treturn s.count\n}\n\nfunc (s *BlockSet) Has(b *BasicBlock) bool {\n\tif b.Index >= len(s.values) {\n\t\treturn false\n\t}\n\treturn s.values[b.Index]\n}\n\n// add adds b to the set and returns true if the set changed.\nfunc (s *BlockSet) Add(b *BasicBlock) bool {\n\tif s.values[b.Index] {\n\t\treturn false\n\t}\n\ts.count++\n\ts.values[b.Index] = true\n\ts.idx = b.Index\n\n\treturn true\n}\n\nfunc (s *BlockSet) Clear() {\n\tfor j := range s.values {\n\t\ts.values[j] = false\n\t}\n\ts.count = 0\n}\n\n// take removes an arbitrary element from a set s and\n// returns its index, or returns -1 if empty.\nfunc (s *BlockSet) Take() int {\n\t// [i, end]\n\tfor i := s.idx; i < len(s.values); i++ {\n\t\tif s.values[i] {\n\t\t\ts.values[i] = false\n\t\t\ts.idx = i\n\t\t\ts.count--\n\t\t\treturn i\n\t\t}\n\t}\n\n\t// [start, i)\n\tfor i := 0; i < s.idx; i++ {\n\t\tif s.values[i] {\n\t\t\ts.values[i] = false\n\t\t\ts.idx = i\n\t\t\ts.count--\n\t\t\treturn i\n\t\t}\n\t}\n\n\treturn -1\n}\n\ntype closure struct {\n\tspan       []uint32\n\treachables BlockMap[interval]\n}\n\ntype interval uint32\n\nconst (\n\tflagMask   = 1 << 31\n\tnumBits    = 20\n\tlengthBits = 32 - numBits - 1\n\tlengthMask = (1<<lengthBits - 1) << numBits\n\tnumMask    = 1<<numBits - 1\n)\n\nfunc (c closure) has(s, v *BasicBlock) bool {\n\tidx := uint32(v.Index)\n\tif idx == 1 || s.Dominates(v) {\n\t\treturn true\n\t}\n\tr := c.reachable(s.Index)\n\tfor i := 0; i < len(r); i++ {\n\t\tinv := r[i]\n\t\tvar start, end uint32\n\t\tif inv&flagMask == 0 {\n\t\t\t// small interval\n\t\t\tstart = uint32(inv & numMask)\n\t\t\tend = start + uint32(inv&lengthMask)>>numBits\n\t\t} else {\n\t\t\t// large interval\n\t\t\ti++\n\t\t\tstart = uint32(inv & numMask)\n\t\t\tend = uint32(r[i])\n\t\t}\n\t\tif idx >= start && idx <= end {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (c closure) reachable(id int) []interval {\n\treturn c.reachables[c.span[id]:c.span[id+1]]\n}\n\nfunc (c closure) walk(current *BasicBlock, b *BasicBlock, visited []bool) {\n\t// TODO(dh): the 'current' argument seems to be unused\n\t// TODO(dh): there's no reason for this to be a method\n\tvisited[b.Index] = true\n\tfor _, succ := range b.Succs {\n\t\tif visited[succ.Index] {\n\t\t\tcontinue\n\t\t}\n\t\tvisited[succ.Index] = true\n\t\tc.walk(current, succ, visited)\n\t}\n}\n\nfunc transitiveClosure(fn *Function) *closure {\n\treachable := make(BlockMap[bool], len(fn.Blocks))\n\tc := &closure{}\n\tc.span = make([]uint32, len(fn.Blocks)+1)\n\n\taddInterval := func(start, end uint32) {\n\t\tif l := end - start; l <= 1<<lengthBits-1 {\n\t\t\tn := interval(l<<numBits | start)\n\t\t\tc.reachables = append(c.reachables, n)\n\t\t} else {\n\t\t\tn1 := interval(1<<31 | start)\n\t\t\tn2 := interval(end)\n\t\t\tc.reachables = append(c.reachables, n1, n2)\n\t\t}\n\t}\n\n\tfor i, b := range fn.Blocks[1:] {\n\t\tfor i := range reachable {\n\t\t\treachable[i] = false\n\t\t}\n\n\t\tc.walk(b, b, reachable)\n\t\tstart := ^uint32(0)\n\t\tfor id, isReachable := range reachable {\n\t\t\tif !isReachable {\n\t\t\t\tif start != ^uint32(0) {\n\t\t\t\t\tend := uint32(id) - 1\n\t\t\t\t\taddInterval(start, end)\n\t\t\t\t\tstart = ^uint32(0)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t} else if start == ^uint32(0) {\n\t\t\t\tstart = uint32(id)\n\t\t\t}\n\t\t}\n\t\tif start != ^uint32(0) {\n\t\t\taddInterval(start, uint32(len(reachable))-1)\n\t\t}\n\n\t\tc.span[i+2] = uint32(len(c.reachables))\n\t}\n\n\treturn c\n}\n\n// newPhi is a pair of a newly introduced φ-node and the lifted Alloc\n// it replaces.\ntype newPhi struct {\n\tphi   *Phi\n\talloc *Alloc\n}\n\ntype newSigma struct {\n\talloc  *Alloc\n\tsigmas []*Sigma\n}\n\ntype liftInstructions struct {\n\tinsertInstructions map[Instruction][]Instruction\n\trenameAllocs       []struct {\n\t\tfrom       *Alloc\n\t\tto         *Alloc\n\t\tstartingAt int\n\t}\n}\n\n// liftable determines if alloc can be lifted, and records instructions to split partially liftable allocs.\n//\n// In the trivial case, all uses of the alloc can be lifted. This is the case when it is only used for storing into and\n// loading from. In that case, no instructions are recorded.\n//\n// In the more complex case, the alloc is used for storing into and loading from, but it is also used as a value, for\n// example because it gets passed to a function, e.g. fn(&x). In this case, uses of the alloc fall into one of two\n// categories: those that can be lifted and those that can't. A boundary forms between these two categories in the\n// function's control flow: Once an unliftable use is encountered, the alloc is no longer liftable for the remainder of\n// the basic block the use is in, nor in any blocks reachable from it.\n//\n// We record instructions that split the alloc into two allocs: one that is used in liftable uses, and one that is used\n// in unliftable uses. Whenever we encounter a boundary between liftable and unliftable uses or blocks, we emit a pair\n// of Load and Store that copy the value from the liftable alloc into the unliftable alloc. Taking these instructions\n// into account, the normal lifting machinery will completely lift the liftable alloc, store the correct lifted values\n// into the unliftable alloc, and will not at all lift the unliftable alloc.\n//\n// In Go syntax, the transformation looks somewhat like this:\n//\n//\tfunc foo() {\n//\t\tx := 32\n//\t\tif cond {\n//\t\t\tprintln(x)\n//\t\t\tescape(&x)\n//\t\t\tprintln(x)\n//\t\t} else {\n//\t\t\tprintln(x)\n//\t\t}\n//\t\tprintln(x)\n//\t}\n//\n// transforms into\n//\n//\tfunc fooSplitAlloc() {\n//\t\tx := 32\n//\t\tvar x_ int\n//\t\tif cond {\n//\t\t\tprintln(x)\n//\t\t\tx_ = x\n//\t\t\tescape(&x_)\n//\t\t\tprintln(x_)\n//\t\t} else {\n//\t\t\tprintln(x)\n//\t\t\tx_ = x\n//\t\t}\n//\t\tprintln(x_)\n//\t}\nfunc liftable(alloc *Alloc, instructions BlockMap[liftInstructions]) bool {\n\tfn := alloc.block.parent\n\n\t// Don't lift result values in functions that defer\n\t// calls that may recover from panic.\n\tif fn.hasDefer {\n\t\tif slices.Contains(fn.results, alloc) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\ttype blockDesc struct {\n\t\t// is the block (partially) unliftable, because it contains unliftable instructions or is reachable by an unliftable block\n\t\tisUnliftable     bool\n\t\thasLiftableLoad  bool\n\t\thasLiftableOther bool\n\t\t// we need to emit stores in predecessors because the unliftable use is in a phi\n\t\tstoreInPreds bool\n\n\t\tlastLiftable    int\n\t\tfirstUnliftable int\n\t}\n\tblocks := make(BlockMap[blockDesc], len(fn.Blocks))\n\tfor _, b := range fn.Blocks {\n\t\tblocks[b.Index].lastLiftable = -1\n\t\tblocks[b.Index].firstUnliftable = len(b.Instrs) + 1\n\t}\n\n\t// Look at all uses of the alloc and deduce which blocks have liftable or unliftable instructions.\n\tfor _, instr := range alloc.referrers {\n\t\t// Find the first unliftable use\n\n\t\tdesc := &blocks[instr.Block().Index]\n\t\thasUnliftable := false\n\t\tinHead := false\n\t\tswitch instr := instr.(type) {\n\t\tcase *Store:\n\t\t\tif instr.Val == alloc {\n\t\t\t\thasUnliftable = true\n\t\t\t}\n\t\tcase *Load:\n\t\tcase *DebugRef:\n\t\tcase *Phi, *Sigma:\n\t\t\tinHead = true\n\t\t\thasUnliftable = true\n\t\tdefault:\n\t\t\thasUnliftable = true\n\t\t}\n\n\t\tif hasUnliftable {\n\t\t\tdesc.isUnliftable = true\n\t\t\tif int(instr.ID()) < desc.firstUnliftable {\n\t\t\t\tdesc.firstUnliftable = int(instr.ID())\n\t\t\t}\n\t\t\tif inHead {\n\t\t\t\tdesc.storeInPreds = true\n\t\t\t\tdesc.firstUnliftable = 0\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, instr := range alloc.referrers {\n\t\t// Find the last liftable use, taking the previously calculated firstUnliftable into consideration\n\n\t\tdesc := &blocks[instr.Block().Index]\n\t\tif int(instr.ID()) >= desc.firstUnliftable {\n\t\t\tcontinue\n\t\t}\n\t\thasLiftable := false\n\t\tswitch instr := instr.(type) {\n\t\tcase *Store:\n\t\t\tif instr.Val != alloc {\n\t\t\t\tdesc.hasLiftableOther = true\n\t\t\t\thasLiftable = true\n\t\t\t}\n\t\tcase *Load:\n\t\t\tdesc.hasLiftableLoad = true\n\t\t\thasLiftable = true\n\t\tcase *DebugRef:\n\t\t\tdesc.hasLiftableOther = true\n\t\t}\n\t\tif hasLiftable {\n\t\t\tif int(instr.ID()) > desc.lastLiftable {\n\t\t\t\tdesc.lastLiftable = int(instr.ID())\n\t\t\t}\n\t\t}\n\t}\n\n\tfor i := range blocks {\n\t\t// Update firstUnliftable to be one after lastLiftable. We do this to include the unliftable's preceding\n\t\t// DebugRefs in the renaming.\n\t\tif blocks[i].lastLiftable == -1 && !blocks[i].storeInPreds {\n\t\t\t// There are no liftable instructions (for this alloc) in this block. Set firstUnliftable to the\n\t\t\t// first non-head instruction to avoid inserting the store before phi instructions, which would\n\t\t\t// fail validation.\n\t\t\tfirst := -1\n\t\tinstrLoop:\n\t\t\tfor i, instr := range fn.Blocks[i].Instrs {\n\t\t\t\tswitch instr.(type) {\n\t\t\t\tcase *Phi, *Sigma:\n\t\t\t\tdefault:\n\t\t\t\t\tfirst = i\n\t\t\t\t\tbreak instrLoop\n\t\t\t\t}\n\t\t\t}\n\t\t\tblocks[i].firstUnliftable = first\n\t\t} else {\n\t\t\tblocks[i].firstUnliftable = blocks[i].lastLiftable + 1\n\t\t}\n\t}\n\n\t// If a block is reachable by a (partially) unliftable block, then the entirety of the block is unliftable. In that\n\t// case, stores have to be inserted in the predecessors.\n\t//\n\t// TODO(dh): this isn't always necessary. If the block is reachable by itself, i.e. part of a loop, then if the\n\t// Alloc instruction is itself part of that loop, then there is a subset of instructions in the loop that can be\n\t// lifted. For example:\n\t//\n\t// \tfor {\n\t// \t\tx := 42\n\t// \t\tprintln(x)\n\t// \t\tescape(&x)\n\t// \t}\n\t//\n\t// The x that escapes in one iteration of the loop isn't the same x that we read from on the next iteration.\n\tseen := make(BlockMap[bool], len(fn.Blocks))\n\tvar dfs func(b *BasicBlock)\n\tdfs = func(b *BasicBlock) {\n\t\tif seen[b.Index] {\n\t\t\treturn\n\t\t}\n\t\tseen[b.Index] = true\n\t\tdesc := &blocks[b.Index]\n\t\tdesc.hasLiftableLoad = false\n\t\tdesc.hasLiftableOther = false\n\t\tdesc.isUnliftable = true\n\t\tdesc.firstUnliftable = 0\n\t\tdesc.storeInPreds = true\n\t\tfor _, succ := range b.Succs {\n\t\t\tdfs(succ)\n\t\t}\n\t}\n\tfor _, b := range fn.Blocks {\n\t\tif blocks[b.Index].isUnliftable {\n\t\t\tfor _, succ := range b.Succs {\n\t\t\t\tdfs(succ)\n\t\t\t}\n\t\t}\n\t}\n\n\thasLiftableLoad := false\n\thasLiftableOther := false\n\thasUnliftable := false\n\tfor _, b := range fn.Blocks {\n\t\tdesc := blocks[b.Index]\n\t\thasLiftableLoad = hasLiftableLoad || desc.hasLiftableLoad\n\t\thasLiftableOther = hasLiftableOther || desc.hasLiftableOther\n\t\tif desc.isUnliftable {\n\t\t\thasUnliftable = true\n\t\t}\n\t}\n\tif !hasLiftableLoad && !hasLiftableOther {\n\t\t// There are no liftable uses\n\t\treturn false\n\t} else if !hasUnliftable {\n\t\t// The alloc is entirely liftable without splitting\n\t\treturn true\n\t} else if !hasLiftableLoad {\n\t\t// The alloc is not entirely liftable, and the only liftable uses are stores. While some of those stores could\n\t\t// get lifted away, it would also lead to an infinite loop when lifting to a fixpoint, because the newly created\n\t\t// allocs also get stored into repeatable and that's their only liftable uses.\n\t\treturn false\n\t}\n\n\t// We need to insert stores for the new alloc. If a (partially) unliftable block has no unliftable\n\t// predecessors and the use isn't in a phi node, then the store can be inserted right before the unliftable use.\n\t// Otherwise, stores have to be inserted at the end of all liftable predecessors.\n\n\tnewAlloc := &Alloc{Heap: true}\n\tnewAlloc.setBlock(alloc.block)\n\tnewAlloc.setType(alloc.typ)\n\tnewAlloc.setSource(alloc.source)\n\tnewAlloc.index = -1\n\tnewAlloc.comment = \"split alloc\"\n\n\t{\n\t\twork := instructions[alloc.block.Index]\n\t\twork.insertInstructions[alloc] = append(work.insertInstructions[alloc], newAlloc)\n\t}\n\n\tpredHasStore := make(BlockMap[bool], len(fn.Blocks))\n\tfor _, b := range fn.Blocks {\n\t\tdesc := &blocks[b.Index]\n\t\tbWork := &instructions[b.Index]\n\n\t\tif desc.isUnliftable {\n\t\t\tbWork.renameAllocs = append(bWork.renameAllocs, struct {\n\t\t\t\tfrom       *Alloc\n\t\t\t\tto         *Alloc\n\t\t\t\tstartingAt int\n\t\t\t}{\n\t\t\t\talloc, newAlloc, int(desc.firstUnliftable),\n\t\t\t})\n\t\t}\n\n\t\tif !desc.isUnliftable {\n\t\t\tcontinue\n\t\t}\n\n\t\tpropagate := func(in *BasicBlock, before Instruction) {\n\t\t\tload := &Load{\n\t\t\t\tX: alloc,\n\t\t\t}\n\t\t\tstore := &Store{\n\t\t\t\tAddr: newAlloc,\n\t\t\t\tVal:  load,\n\t\t\t}\n\t\t\tload.setType(deref(alloc.typ))\n\t\t\tload.setBlock(in)\n\t\t\tload.comment = \"split alloc\"\n\t\t\tstore.setBlock(in)\n\t\t\tupdateOperandReferrers(load)\n\t\t\tupdateOperandReferrers(store)\n\t\t\tstore.comment = \"split alloc\"\n\n\t\t\tentry := &instructions[in.Index]\n\t\t\tentry.insertInstructions[before] = append(entry.insertInstructions[before], load, store)\n\t\t}\n\n\t\tif desc.storeInPreds {\n\t\t\t// emit stores at the end of liftable preds\n\t\t\tfor _, pred := range b.Preds {\n\t\t\t\tif blocks[pred.Index].isUnliftable {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif !alloc.block.Dominates(pred) {\n\t\t\t\t\t// Consider this cfg:\n\t\t\t\t\t//\n\t\t\t\t\t//      1\n\t\t\t\t\t//     /|\n\t\t\t\t\t//    / |\n\t\t\t\t\t//   ↙  ↓\n\t\t\t\t\t//  2--→3\n\t\t\t\t\t//\n\t\t\t\t\t// with an Alloc in block 2. It doesn't make sense to insert a store in block 1 for the jump to\n\t\t\t\t\t// block 3, because 1 can never see the Alloc in the first place.\n\t\t\t\t\t//\n\t\t\t\t\t// Ignoring phi nodes, an Alloc always dominates all of its uses, and phi nodes don't matter here,\n\t\t\t\t\t// because for the incoming edges that do matter, we do emit the stores.\n\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif predHasStore[pred.Index] {\n\t\t\t\t\t// Don't generate redundant propagations. Not only is it unnecessary, it can lead to infinite loops\n\t\t\t\t\t// when trying to lift to a fix point, because redundant stores are liftable.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tpredHasStore[pred.Index] = true\n\n\t\t\t\tbefore := pred.Instrs[len(pred.Instrs)-1]\n\t\t\t\tpropagate(pred, before)\n\t\t\t}\n\t\t} else {\n\t\t\t// emit store before the first unliftable use\n\t\t\tbefore := b.Instrs[desc.firstUnliftable]\n\t\t\tpropagate(b, before)\n\t\t}\n\t}\n\n\treturn true\n}\n\n// liftAlloc lifts alloc into registers and populates newPhis and newSigmas with all the φ- and σ-nodes it may require.\nfunc liftAlloc(closure *closure, df domFrontier, rdf postDomFrontier, alloc *Alloc, newPhis BlockMap[[]newPhi], newSigmas BlockMap[[]newSigma]) {\n\tfn := alloc.Parent()\n\n\tdefblocks := fn.blockset(0)\n\tuseblocks := fn.blockset(1)\n\tAphi := fn.blockset(2)\n\tAsigma := fn.blockset(3)\n\tW := fn.blockset(4)\n\n\t// Compute defblocks, the set of blocks containing a\n\t// definition of the alloc cell.\n\tfor _, instr := range *alloc.Referrers() {\n\t\tswitch instr := instr.(type) {\n\t\tcase *Store:\n\t\t\tdefblocks.Add(instr.Block())\n\t\tcase *Load:\n\t\t\tuseblocks.Add(instr.Block())\n\t\t\tfor _, ref := range *instr.Referrers() {\n\t\t\t\tuseblocks.Add(ref.Block())\n\t\t\t}\n\t\t}\n\t}\n\t// The Alloc itself counts as a (zero) definition of the cell.\n\tdefblocks.Add(alloc.Block())\n\n\tif debugLifting {\n\t\tfmt.Fprintln(os.Stderr, \"\\tlifting \", alloc, alloc.Name())\n\t}\n\n\t// Φ-insertion.\n\t//\n\t// What follows is the body of the main loop of the insert-φ\n\t// function described by Cytron et al, but instead of using\n\t// counter tricks, we just reset the 'hasAlready' and 'work'\n\t// sets each iteration.  These are bitmaps so it's pretty cheap.\n\n\t// Initialize W and work to defblocks.\n\n\tfor change := true; change; {\n\t\tchange = false\n\t\t{\n\t\t\t// Traverse iterated dominance frontier, inserting φ-nodes.\n\t\t\tW.Set(defblocks)\n\n\t\t\tfor i := W.Take(); i != -1; i = W.Take() {\n\t\t\t\tn := fn.Blocks[i]\n\t\t\t\tfor _, y := range df[n.Index] {\n\t\t\t\t\tif Aphi.Add(y) {\n\t\t\t\t\t\tif len(*alloc.Referrers()) == 0 {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlive := false\n\t\t\t\t\t\tif closure == nil {\n\t\t\t\t\t\t\tlive = true\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfor _, ref := range *alloc.Referrers() {\n\t\t\t\t\t\t\t\tif _, ok := ref.(*Load); ok {\n\t\t\t\t\t\t\t\t\tif closure.has(y, ref.Block()) {\n\t\t\t\t\t\t\t\t\t\tlive = true\n\t\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif !live {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Create φ-node.\n\t\t\t\t\t\t// It will be prepended to v.Instrs later, if needed.\n\t\t\t\t\t\tif len(y.Preds) == 0 {\n\t\t\t\t\t\t\t// The exit block may be unreachable if the function doesn't\n\t\t\t\t\t\t\t// return, e.g. due to an infinite loop. In that case we\n\t\t\t\t\t\t\t// should not replace loads in the exit block with ϕ node that\n\t\t\t\t\t\t\t// have no edges. Such loads exist when the function has named\n\t\t\t\t\t\t\t// return parameters, as the exit block loads them to turn\n\t\t\t\t\t\t\t// them into a Return instruction. By not replacing the loads\n\t\t\t\t\t\t\t// with ϕ nodes, they will later be replaced by zero\n\t\t\t\t\t\t\t// constants. This is arguably more correct, and more\n\t\t\t\t\t\t\t// importantly, it doesn't break code that assumes that phis\n\t\t\t\t\t\t\t// have at least one edge.\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// For one instance of breakage see\n\t\t\t\t\t\t\t// https://staticcheck.dev/issues/1533\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tphi := &Phi{\n\t\t\t\t\t\t\tEdges: make([]Value, len(y.Preds)),\n\t\t\t\t\t\t}\n\t\t\t\t\t\tphi.comment = alloc.comment\n\t\t\t\t\t\tphi.source = alloc.source\n\t\t\t\t\t\tphi.setType(deref(alloc.Type()))\n\t\t\t\t\t\tphi.block = y\n\t\t\t\t\t\tif debugLifting {\n\t\t\t\t\t\t\tfmt.Fprintf(os.Stderr, \"\\tplace %s = %s at block %s\\n\", phi.Name(), phi, y)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnewPhis[y.Index] = append(newPhis[y.Index], newPhi{phi, alloc})\n\n\t\t\t\t\t\tfor _, p := range y.Preds {\n\t\t\t\t\t\t\tuseblocks.Add(p)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tchange = true\n\t\t\t\t\t\tif defblocks.Add(y) {\n\t\t\t\t\t\t\tW.Add(y)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t{\n\t\t\tW.Set(useblocks)\n\t\t\tfor i := W.Take(); i != -1; i = W.Take() {\n\t\t\t\tn := fn.Blocks[i]\n\t\t\t\tfor _, y := range rdf[n.Index] {\n\t\t\t\t\tif Asigma.Add(y) {\n\t\t\t\t\t\tsigmas := make([]*Sigma, 0, len(y.Succs))\n\t\t\t\t\t\tanyLive := false\n\t\t\t\t\t\tfor _, succ := range y.Succs {\n\t\t\t\t\t\t\tlive := false\n\t\t\t\t\t\t\tfor _, ref := range *alloc.Referrers() {\n\t\t\t\t\t\t\t\tif closure == nil || closure.has(succ, ref.Block()) {\n\t\t\t\t\t\t\t\t\tlive = true\n\t\t\t\t\t\t\t\t\tanyLive = true\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif live {\n\t\t\t\t\t\t\t\tsigma := &Sigma{\n\t\t\t\t\t\t\t\t\tFrom: y,\n\t\t\t\t\t\t\t\t\tX:    alloc,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsigma.comment = alloc.comment\n\t\t\t\t\t\t\t\tsigma.source = alloc.source\n\t\t\t\t\t\t\t\tsigma.setType(deref(alloc.Type()))\n\t\t\t\t\t\t\t\tsigma.block = succ\n\t\t\t\t\t\t\t\tsigmas = append(sigmas, sigma)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsigmas = append(sigmas, nil)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif anyLive {\n\t\t\t\t\t\t\tnewSigmas[y.Index] = append(newSigmas[y.Index], newSigma{alloc, sigmas})\n\t\t\t\t\t\t\tfor _, s := range y.Succs {\n\t\t\t\t\t\t\t\tdefblocks.Add(s)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tchange = true\n\t\t\t\t\t\t\tif useblocks.Add(y) {\n\t\t\t\t\t\t\t\tW.Add(y)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// replaceAll replaces all intraprocedural uses of x with y,\n// updating x.Referrers and y.Referrers.\n// Precondition: x.Referrers() != nil, i.e. x must be local to some function.\nfunc replaceAll(x, y Value) {\n\tvar rands []*Value\n\tpxrefs := x.Referrers()\n\tpyrefs := y.Referrers()\n\tfor _, instr := range *pxrefs {\n\t\tswitch instr := instr.(type) {\n\t\tcase *CompositeValue:\n\t\t\t// Special case CompositeValue because it might have very large lists of operands\n\t\t\t//\n\t\t\t// OPT(dh): this loop is still expensive for large composite values\n\t\t\tfor i, rand := range instr.Values {\n\t\t\t\tif rand == x {\n\t\t\t\t\tinstr.Values[i] = y\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\trands = instr.Operands(rands[:0]) // recycle storage\n\t\t\tfor _, rand := range rands {\n\t\t\t\tif *rand != nil {\n\t\t\t\t\tif *rand == x {\n\t\t\t\t\t\t*rand = y\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif pyrefs != nil {\n\t\t\t*pyrefs = append(*pyrefs, instr) // dups ok\n\t\t}\n\t}\n\t*pxrefs = nil // x is now unreferenced\n}\n\nfunc replace(instr Instruction, x, y Value) {\n\targs := instr.Operands(nil)\n\tmatched := false\n\tfor _, arg := range args {\n\t\tif *arg == x {\n\t\t\t*arg = y\n\t\t\tmatched = true\n\t\t}\n\t}\n\tif matched {\n\t\tyrefs := y.Referrers()\n\t\tif yrefs != nil {\n\t\t\t*yrefs = append(*yrefs, instr)\n\t\t}\n\n\t\txrefs := x.Referrers()\n\t\tif xrefs != nil {\n\t\t\t*xrefs = removeInstr(*xrefs, instr)\n\t\t}\n\t}\n}\n\n// renamed returns the value to which alloc is being renamed,\n// constructing it lazily if it's the implicit zero initialization.\nfunc renamed(fn *Function, renaming []Value, alloc *Alloc) Value {\n\tv := renaming[alloc.index]\n\tif v == nil {\n\t\tv = emitConst(fn, zeroConst(deref(alloc.Type()), alloc.source))\n\t\trenaming[alloc.index] = v\n\t}\n\treturn v\n}\n\nfunc copyValue(v Value, why Instruction, info CopyInfo) *Copy {\n\tc := &Copy{\n\t\tX:    v,\n\t\tWhy:  why,\n\t\tInfo: info,\n\t}\n\tif refs := v.Referrers(); refs != nil {\n\t\t*refs = append(*refs, c)\n\t}\n\tc.setType(v.Type())\n\tc.setSource(v.Source())\n\treturn c\n}\n\nfunc splitOnNewInformation(u *BasicBlock, renaming *StackMap) {\n\trenaming.Push()\n\tdefer renaming.Pop()\n\n\trename := func(v Value, why Instruction, info CopyInfo, i int) {\n\t\tc := copyValue(v, why, info)\n\t\tc.setBlock(u)\n\t\trenaming.Set(v, c)\n\t\tu.Instrs = append(u.Instrs, nil)\n\t\tcopy(u.Instrs[i+2:], u.Instrs[i+1:])\n\t\tu.Instrs[i+1] = c\n\t}\n\n\treplacement := func(v Value) (Value, bool) {\n\t\tr, ok := renaming.Get(v)\n\t\tif !ok {\n\t\t\treturn nil, false\n\t\t}\n\t\tfor {\n\t\t\trr, ok := renaming.Get(r)\n\t\t\tif !ok {\n\t\t\t\t// Store replacement in the map so that future calls to replacement(v) don't have to go through the\n\t\t\t\t// iterative process again.\n\t\t\t\trenaming.Set(v, r)\n\t\t\t\treturn r, true\n\t\t\t}\n\t\t\tr = rr\n\t\t}\n\t}\n\n\tvar hasInfo func(v Value, info CopyInfo) bool\n\thasInfo = func(v Value, info CopyInfo) bool {\n\t\tswitch v := v.(type) {\n\t\tcase *Copy:\n\t\t\treturn (v.Info&info) == info || hasInfo(v.X, info)\n\t\tcase *FieldAddr, *IndexAddr, *TypeAssert, *MakeChan, *MakeMap, *MakeSlice, *Alloc:\n\t\t\treturn info == CopyInfoNotNil\n\t\tcase Member, *Builtin:\n\t\t\treturn info == CopyInfoNotNil\n\t\tcase *Sigma:\n\t\t\treturn hasInfo(v.X, info)\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\t}\n\n\tvar args []*Value\n\tfor i := 0; i < len(u.Instrs); i++ {\n\t\tinstr := u.Instrs[i]\n\t\tif instr == nil {\n\t\t\tcontinue\n\t\t}\n\t\targs = instr.Operands(args[:0])\n\t\tfor _, arg := range args {\n\t\t\tif *arg == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif r, ok := replacement(*arg); ok {\n\t\t\t\t*arg = r\n\t\t\t\treplace(instr, *arg, r)\n\t\t\t}\n\t\t}\n\n\t\t// TODO write some bits on why we copy values instead of encoding the actual control flow and panics\n\n\t\tswitch instr := instr.(type) {\n\t\tcase *IndexAddr:\n\t\t\t// Note that we rename instr.Index and instr.X even if they're already copies, because unique combinations\n\t\t\t// of X and Index may lead to unique information.\n\n\t\t\t// OPT we should rename both variables at once and avoid one memmove\n\t\t\trename(instr.Index, instr, CopyInfoNotNegative, i)\n\t\t\trename(instr.X, instr, CopyInfoNotNil, i)\n\t\t\ti += 2 // skip over instructions we just inserted\n\t\tcase *FieldAddr:\n\t\t\tif !hasInfo(instr.X, CopyInfoNotNil) {\n\t\t\t\trename(instr.X, instr, CopyInfoNotNil, i)\n\t\t\t\ti++\n\t\t\t}\n\t\tcase *TypeAssert:\n\t\t\t// If we've already type asserted instr.X without comma-ok before, then it can only contain a single type,\n\t\t\t// and successive type assertions, no matter the type, don't tell us anything new.\n\t\t\tif !hasInfo(instr.X, CopyInfoNotNil|CopyInfoSingleConcreteType) {\n\t\t\t\trename(instr.X, instr, CopyInfoNotNil|CopyInfoSingleConcreteType, i)\n\t\t\t\ti++ // skip over instruction we just inserted\n\t\t\t}\n\t\tcase *Load:\n\t\t\tif !hasInfo(instr.X, CopyInfoNotNil) {\n\t\t\t\trename(instr.X, instr, CopyInfoNotNil, i)\n\t\t\t\ti++\n\t\t\t}\n\t\tcase *Store:\n\t\t\tif !hasInfo(instr.Addr, CopyInfoNotNil) {\n\t\t\t\trename(instr.Addr, instr, CopyInfoNotNil, i)\n\t\t\t\ti++\n\t\t\t}\n\t\tcase *MapUpdate:\n\t\t\tif !hasInfo(instr.Map, CopyInfoNotNil) {\n\t\t\t\trename(instr.Map, instr, CopyInfoNotNil, i)\n\t\t\t\ti++\n\t\t\t}\n\t\tcase CallInstruction:\n\t\t\toff := 0\n\t\t\tif !instr.Common().IsInvoke() && !hasInfo(instr.Common().Value, CopyInfoNotNil) {\n\t\t\t\trename(instr.Common().Value, instr, CopyInfoNotNil, i)\n\t\t\t\toff++\n\t\t\t}\n\t\t\tif f, ok := instr.Common().Value.(*Builtin); ok {\n\t\t\t\tswitch f.name {\n\t\t\t\tcase \"close\":\n\t\t\t\t\targ := instr.Common().Args[0]\n\t\t\t\t\tif !hasInfo(arg, CopyInfoNotNil|CopyInfoClosed) {\n\t\t\t\t\t\trename(arg, instr, CopyInfoNotNil|CopyInfoClosed, i)\n\t\t\t\t\t\toff++\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\ti += off\n\t\tcase *SliceToArrayPointer:\n\t\t\t// A slice to array pointer conversion tells us the minimum length of the slice\n\t\t\trename(instr.X, instr, CopyInfoUnspecified, i)\n\t\t\ti++\n\t\tcase *SliceToArray:\n\t\t\t// A slice to array conversion tells us the minimum length of the slice\n\t\t\trename(instr.X, instr, CopyInfoUnspecified, i)\n\t\t\ti++\n\t\tcase *Slice:\n\t\t\t// Slicing tells us about some of the bounds\n\t\t\toff := 0\n\t\t\tif instr.Low == nil && instr.High == nil && instr.Max == nil {\n\t\t\t\t// If all indices are unspecified, then we can only learn something about instr.X if it might've been\n\t\t\t\t// nil.\n\t\t\t\tif !hasInfo(instr.X, CopyInfoNotNil) {\n\t\t\t\t\trename(instr.X, instr, CopyInfoUnspecified, i)\n\t\t\t\t\toff++\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trename(instr.X, instr, CopyInfoUnspecified, i)\n\t\t\t\toff++\n\t\t\t}\n\t\t\t// We copy the indices even if we already know they are not negative, because we can associate numeric\n\t\t\t// ranges with them.\n\t\t\tif instr.Low != nil {\n\t\t\t\trename(instr.Low, instr, CopyInfoNotNegative, i)\n\t\t\t\toff++\n\t\t\t}\n\t\t\tif instr.High != nil {\n\t\t\t\trename(instr.High, instr, CopyInfoNotNegative, i)\n\t\t\t\toff++\n\t\t\t}\n\t\t\tif instr.Max != nil {\n\t\t\t\trename(instr.Max, instr, CopyInfoNotNegative, i)\n\t\t\t\toff++\n\t\t\t}\n\t\t\ti += off\n\t\tcase *StringLookup:\n\t\t\trename(instr.X, instr, CopyInfoUnspecified, i)\n\t\t\trename(instr.Index, instr, CopyInfoNotNegative, i)\n\t\t\ti += 2\n\t\tcase *Recv:\n\t\t\tif !hasInfo(instr.Chan, CopyInfoNotNil) {\n\t\t\t\t// Receiving from a nil channel never completes\n\t\t\t\trename(instr.Chan, instr, CopyInfoNotNil, i)\n\t\t\t\ti++\n\t\t\t}\n\t\tcase *Send:\n\t\t\tif !hasInfo(instr.Chan, CopyInfoNotNil) {\n\t\t\t\t// Sending to a nil channel never completes. Sending to a closed channel panics, but whether a channel\n\t\t\t\t// is closed isn't local to this function, so we didn't learn anything.\n\t\t\t\trename(instr.Chan, instr, CopyInfoNotNil, i)\n\t\t\t\ti++\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, v := range u.dom.children {\n\t\tsplitOnNewInformation(v, renaming)\n\t}\n}\n\n// rename implements the Cytron et al-based SSI renaming algorithm, a\n// preorder traversal of the dominator tree replacing all loads of\n// Alloc cells with the value stored to that cell by the dominating\n// store instruction.\n//\n// renaming is a map from *Alloc (keyed by index number) to its\n// dominating stored value; newPhis[x] is the set of new φ-nodes to be\n// prepended to block x.\nfunc rename(u *BasicBlock, renaming []Value, newPhis BlockMap[[]newPhi], newSigmas BlockMap[[]newSigma]) {\n\t// Each φ-node becomes the new name for its associated Alloc.\n\tfor _, np := range newPhis[u.Index] {\n\t\tphi := np.phi\n\t\talloc := np.alloc\n\t\trenaming[alloc.index] = phi\n\t}\n\n\t// Rename loads and stores of allocs.\n\tfor i, instr := range u.Instrs {\n\t\tswitch instr := instr.(type) {\n\t\tcase *Alloc:\n\t\t\tif instr.index >= 0 { // store of zero to Alloc cell\n\t\t\t\t// Replace dominated loads by the zero value.\n\t\t\t\trenaming[instr.index] = nil\n\t\t\t\tif debugLifting {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"\\tkill alloc %s\\n\", instr)\n\t\t\t\t}\n\t\t\t\t// Delete the Alloc.\n\t\t\t\tu.Instrs[i] = nil\n\t\t\t\tu.gaps++\n\t\t\t}\n\n\t\tcase *Store:\n\t\t\tif alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell\n\t\t\t\t// Replace dominated loads by the stored value.\n\t\t\t\trenaming[alloc.index] = instr.Val\n\t\t\t\tif debugLifting {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"\\tkill store %s; new value: %s\\n\",\n\t\t\t\t\t\tinstr, instr.Val.Name())\n\t\t\t\t}\n\t\t\t\tif refs := instr.Addr.Referrers(); refs != nil {\n\t\t\t\t\t*refs = removeInstr(*refs, instr)\n\t\t\t\t}\n\t\t\t\tif refs := instr.Val.Referrers(); refs != nil {\n\t\t\t\t\t*refs = removeInstr(*refs, instr)\n\t\t\t\t}\n\t\t\t\t// Delete the Store.\n\t\t\t\tu.Instrs[i] = nil\n\t\t\t\tu.gaps++\n\t\t\t}\n\n\t\tcase *Load:\n\t\t\tif alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell\n\t\t\t\t// In theory, we wouldn't be able to replace loads directly, because a loaded value could be used in\n\t\t\t\t// different branches, in which case it should be replaced with different sigma nodes. But we can't\n\t\t\t\t// simply defer replacement, either, because then later stores might incorrectly affect this load.\n\t\t\t\t//\n\t\t\t\t// To avoid doing renaming on _all_ values (instead of just loads and stores like we're doing), we make\n\t\t\t\t// sure during code generation that each load is only used in one block. For example, in constant switch\n\t\t\t\t// statements, where the tag is only evaluated once, we store it in a temporary and load it for each\n\t\t\t\t// comparison, so that we have individual loads to replace.\n\t\t\t\t//\n\t\t\t\t// Because we only rename stores and loads, the end result will not contain sigma nodes for all\n\t\t\t\t// constants. Some constants may be used directly, e.g. in comparisons such as 'x == 5'. We may still\n\t\t\t\t// end up inserting dead sigma nodes in branches, but these will never get used in renaming and will be\n\t\t\t\t// cleaned up when we remove dead phis and sigmas.\n\t\t\t\tnewval := renamed(u.Parent(), renaming, alloc)\n\t\t\t\tif debugLifting {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"\\tupdate load %s = %s with %s\\n\",\n\t\t\t\t\t\tinstr.Name(), instr, newval)\n\t\t\t\t}\n\t\t\t\treplaceAll(instr, newval)\n\t\t\t\tu.Instrs[i] = nil\n\t\t\t\tu.gaps++\n\t\t\t}\n\n\t\tcase *DebugRef:\n\t\t\tif x, ok := instr.X.(*Alloc); ok && x.index >= 0 {\n\t\t\t\tif instr.IsAddr {\n\t\t\t\t\tinstr.X = renamed(u.Parent(), renaming, x)\n\t\t\t\t\tinstr.IsAddr = false\n\n\t\t\t\t\t// Add DebugRef to instr.X's referrers.\n\t\t\t\t\tif refs := instr.X.Referrers(); refs != nil {\n\t\t\t\t\t\t*refs = append(*refs, instr)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// A source expression denotes the address\n\t\t\t\t\t// of an Alloc that was optimized away.\n\t\t\t\t\tinstr.X = nil\n\n\t\t\t\t\t// Delete the DebugRef.\n\t\t\t\t\tu.Instrs[i] = nil\n\t\t\t\t\tu.gaps++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// update all outgoing sigma nodes with the dominating store\n\tfor _, sigmas := range newSigmas[u.Index] {\n\t\tfor _, sigma := range sigmas.sigmas {\n\t\t\tif sigma == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsigma.X = renamed(u.Parent(), renaming, sigmas.alloc)\n\t\t}\n\t}\n\n\t// For each φ-node in a CFG successor, rename the edge.\n\tfor succi, v := range u.Succs {\n\t\tphis := newPhis[v.Index]\n\t\tif len(phis) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\ti := v.predIndex(u)\n\t\tfor _, np := range phis {\n\t\t\tphi := np.phi\n\t\t\talloc := np.alloc\n\t\t\t// if there's a sigma node, use it, else use the dominating value\n\t\t\tvar newval Value\n\t\t\tfor _, sigmas := range newSigmas[u.Index] {\n\t\t\t\tif sigmas.alloc == alloc && sigmas.sigmas[succi] != nil {\n\t\t\t\t\tnewval = sigmas.sigmas[succi]\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif newval == nil {\n\t\t\t\tnewval = renamed(u.Parent(), renaming, alloc)\n\t\t\t}\n\t\t\tif debugLifting {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"\\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\\n\",\n\t\t\t\t\tphi.Name(), u, v, i, alloc.Name(), newval.Name())\n\t\t\t}\n\t\t\tphi.Edges[i] = newval\n\t\t\tif prefs := newval.Referrers(); prefs != nil {\n\t\t\t\t*prefs = append(*prefs, phi)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Continue depth-first recursion over domtree, pushing a\n\t// fresh copy of the renaming map for each subtree.\n\tr := make([]Value, len(renaming))\n\tfor _, v := range u.dom.children {\n\t\tcopy(r, renaming)\n\n\t\t// on entry to a block, the incoming sigma nodes become the new values for their alloc\n\t\tif idx := u.succIndex(v); idx != -1 {\n\t\t\tfor _, sigma := range newSigmas[u.Index] {\n\t\t\t\tif sigma.sigmas[idx] != nil {\n\t\t\t\t\tr[sigma.alloc.index] = sigma.sigmas[idx]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\trename(v, r, newPhis, newSigmas)\n\t}\n\n}\n\nfunc simplifyConstantCompositeValues(fn *Function) bool {\n\tchanged := false\n\n\tfor _, b := range fn.Blocks {\n\t\tn := 0\n\t\tfor _, instr := range b.Instrs {\n\t\t\treplaced := false\n\n\t\t\tif cv, ok := instr.(*CompositeValue); ok {\n\t\t\t\tac := &AggregateConst{}\n\t\t\t\tac.typ = cv.typ\n\t\t\t\treplaced = true\n\t\t\t\tfor _, v := range cv.Values {\n\t\t\t\t\tif c, ok := v.(Constant); ok {\n\t\t\t\t\t\tac.Values = append(ac.Values, c)\n\t\t\t\t\t} else {\n\t\t\t\t\t\treplaced = false\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif replaced {\n\t\t\t\t\treplaceAll(cv, emitConst(fn, ac))\n\t\t\t\t\tkillInstruction(cv)\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif replaced {\n\t\t\t\tchanged = true\n\t\t\t} else {\n\t\t\t\tb.Instrs[n] = instr\n\t\t\t\tn++\n\t\t\t}\n\t\t}\n\n\t\tclearInstrs(b.Instrs[n:])\n\t\tb.Instrs = b.Instrs[:n]\n\t}\n\n\treturn changed\n}\n\nfunc updateOperandReferrers(instr Instruction) {\n\tfor _, op := range instr.Operands(nil) {\n\t\trefs := (*op).Referrers()\n\t\tif refs != nil {\n\t\t\t*refs = append(*refs, instr)\n\t\t}\n\t}\n}\n\n// deferstackPreamble returns the *Alloc and ssa:deferstack() call for fn.deferstack.\nfunc deferstackPreamble(fn *Function) (*Alloc, *Call) {\n\tif alloc, _ := fn.vars[fn.deferstack].(*Alloc); alloc != nil {\n\t\tfor _, ref := range *alloc.Referrers() {\n\t\t\tif ref, _ := ref.(*Store); ref != nil && ref.Addr == alloc {\n\t\t\t\tif call, _ := ref.Val.(*Call); call != nil {\n\t\t\t\t\treturn alloc, call\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "go/ir/lvalue.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// lvalues are the union of addressable expressions and map-index\n// expressions.\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n)\n\n// An lvalue represents an assignable location that may appear on the\n// left-hand side of an assignment.  This is a generalization of a\n// pointer to permit updates to elements of maps.\ntype lvalue interface {\n\tstore(fn *Function, v Value, source ast.Node) // stores v into the location\n\tload(fn *Function, source ast.Node) Value     // loads the contents of the location\n\taddress(fn *Function) Value                   // address of the location\n\ttyp() types.Type                              // returns the type of the location\n}\n\n// An address is an lvalue represented by a true pointer.\ntype address struct {\n\taddr Value\n\texpr ast.Expr // source syntax of the value (not address) [debug mode]\n}\n\nfunc (a *address) load(fn *Function, source ast.Node) Value {\n\treturn emitLoad(fn, a.addr, source)\n}\n\nfunc (a *address) store(fn *Function, v Value, source ast.Node) {\n\tstore := emitStore(fn, a.addr, v, source)\n\tif a.expr != nil {\n\t\t// store.Val is v, converted for assignability.\n\t\temitDebugRef(fn, a.expr, store.Val, false)\n\t}\n}\n\nfunc (a *address) address(fn *Function) Value {\n\tif a.expr != nil {\n\t\temitDebugRef(fn, a.expr, a.addr, true)\n\t}\n\treturn a.addr\n}\n\nfunc (a *address) typ() types.Type {\n\treturn deref(a.addr.Type())\n}\n\ntype compositeElement struct {\n\tcv   *CompositeValue\n\tidx  int\n\tt    types.Type\n\texpr ast.Expr\n}\n\nfunc (ce *compositeElement) load(fn *Function, source ast.Node) Value {\n\tpanic(\"not implemented\")\n}\n\nfunc (ce *compositeElement) store(fn *Function, v Value, source ast.Node) {\n\tv = emitConv(fn, v, ce.t, source)\n\tce.cv.Values[ce.idx] = v\n\tif ce.expr != nil {\n\t\t// store.Val is v, converted for assignability.\n\t\temitDebugRef(fn, ce.expr, v, false)\n\t}\n}\n\nfunc (ce *compositeElement) address(fn *Function) Value {\n\tpanic(\"not implemented\")\n}\n\nfunc (ce *compositeElement) typ() types.Type {\n\treturn ce.t\n}\n\n// An element is an lvalue represented by m[k], the location of an\n// element of a map.  These locations are not addressable\n// since pointers cannot be formed from them, but they do support\n// load() and store().\ntype element struct {\n\tm, k Value      // map\n\tt    types.Type // map element type\n}\n\nfunc (e *element) load(fn *Function, source ast.Node) Value {\n\tl := &MapLookup{\n\t\tX:     e.m,\n\t\tIndex: e.k,\n\t}\n\tl.setType(e.t)\n\treturn fn.emit(l, source)\n}\n\nfunc (e *element) store(fn *Function, v Value, source ast.Node) {\n\tup := &MapUpdate{\n\t\tMap:   e.m,\n\t\tKey:   e.k,\n\t\tValue: emitConv(fn, v, e.t, source),\n\t}\n\tfn.emit(up, source)\n}\n\nfunc (e *element) address(fn *Function) Value {\n\tpanic(\"map elements are not addressable\")\n}\n\nfunc (e *element) typ() types.Type {\n\treturn e.t\n}\n\n// A lazyAddress is an lvalue whose address is the result of an instruction.\n// These work like an *address except a new address.address() Value\n// is created on each load, store and address call.\n// A lazyAddress can be used to control when a side effect (nil pointer\n// dereference, index out of bounds) of using a location happens.\ntype lazyAddress struct {\n\taddr func(fn *Function) Value // emit to fn the computation of the address\n\tt    types.Type               // type of the location\n\texpr ast.Expr                 // source syntax of the value (not address) [debug mode]\n}\n\nfunc (l *lazyAddress) load(fn *Function, source ast.Node) Value {\n\tload := emitLoad(fn, l.addr(fn), source)\n\treturn load\n}\n\nfunc (l *lazyAddress) store(fn *Function, v Value, source ast.Node) {\n\tstore := emitStore(fn, l.addr(fn), v, source)\n\tif l.expr != nil {\n\t\t// store.Val is v, converted for assignability.\n\t\temitDebugRef(fn, l.expr, store.Val, false)\n\t}\n}\n\nfunc (l *lazyAddress) address(fn *Function) Value {\n\taddr := l.addr(fn)\n\tif l.expr != nil {\n\t\temitDebugRef(fn, l.expr, addr, true)\n\t}\n\treturn addr\n}\n\nfunc (l *lazyAddress) typ() types.Type { return l.t }\n\n// A blank is a dummy variable whose name is \"_\".\n// It is not reified: loads are illegal and stores are ignored.\ntype blank struct{}\n\nfunc (bl blank) load(fn *Function, source ast.Node) Value {\n\tpanic(\"blank.load is illegal\")\n}\n\nfunc (bl blank) store(fn *Function, v Value, source ast.Node) {\n\ts := &BlankStore{\n\t\tVal: v,\n\t}\n\tfn.emit(s, source)\n}\n\nfunc (bl blank) address(fn *Function) Value {\n\tpanic(\"blank var is not addressable\")\n}\n\nfunc (bl blank) typ() types.Type {\n\t// This should be the type of the blank Ident; the typechecker\n\t// doesn't provide this yet, but fortunately, we don't need it\n\t// yet either.\n\tpanic(\"blank.typ is unimplemented\")\n}\n"
  },
  {
    "path": "go/ir/methods.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file defines utilities for population of method sets.\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n)\n\n// MethodValue returns the Function implementing method sel, building\n// wrapper methods on demand.  It returns nil if sel denotes an\n// abstract (interface) method.\n//\n// Precondition: sel.Kind() == MethodVal.\n//\n// Thread-safe.\n//\n// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)\nfunc (prog *Program) MethodValue(sel *types.Selection) *Function {\n\tif sel.Kind() != types.MethodVal {\n\t\tpanic(fmt.Sprintf(\"MethodValue(%s) kind != MethodVal\", sel))\n\t}\n\tT := sel.Recv()\n\tif types.IsInterface(T) {\n\t\treturn nil // abstract method\n\t}\n\tif prog.mode&LogSource != 0 {\n\t\tdefer logStack(\"MethodValue %s %v\", T, sel)()\n\t}\n\n\tprog.methodsMu.Lock()\n\tdefer prog.methodsMu.Unlock()\n\n\treturn prog.addMethod(prog.createMethodSet(T), sel)\n}\n\n// LookupMethod returns the implementation of the method of type T\n// identified by (pkg, name).  It returns nil if the method exists but\n// is abstract, and panics if T has no such method.\nfunc (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {\n\tsel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)\n\tif sel == nil {\n\t\tpanic(fmt.Sprintf(\"%s has no method %s\", T, types.Id(pkg, name)))\n\t}\n\treturn prog.MethodValue(sel)\n}\n\n// methodSet contains the (concrete) methods of a non-interface type.\ntype methodSet struct {\n\tmapping  map[string]*Function // populated lazily\n\tcomplete bool                 // mapping contains all methods\n}\n\n// Precondition: !isInterface(T).\n// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)\nfunc (prog *Program) createMethodSet(T types.Type) *methodSet {\n\tmset, ok := prog.methodSets.At(T)\n\tif !ok {\n\t\tmset = &methodSet{mapping: make(map[string]*Function)}\n\t\tprog.methodSets.Set(T, mset)\n\t}\n\treturn mset\n}\n\n// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)\nfunc (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function {\n\tif sel.Kind() == types.MethodExpr {\n\t\tpanic(sel)\n\t}\n\tid := sel.Obj().Id()\n\tfn := mset.mapping[id]\n\tif fn == nil {\n\t\tobj := sel.Obj().(*types.Func)\n\n\t\tneedsPromotion := len(sel.Index()) > 1\n\t\tneedsIndirection := !isPointer(recvType(obj)) && isPointer(sel.Recv())\n\t\tif needsPromotion || needsIndirection {\n\t\t\tfn = makeWrapper(prog, sel)\n\t\t} else {\n\t\t\tfn = prog.declaredFunc(obj)\n\t\t}\n\t\tif fn.Signature.Recv() == nil {\n\t\t\tpanic(fn) // missing receiver\n\t\t}\n\t\tmset.mapping[id] = fn\n\t}\n\treturn fn\n}\n\n// RuntimeTypes returns a new unordered slice containing all\n// concrete types in the program for which a complete (non-empty)\n// method set is required at run-time.\n//\n// Thread-safe.\n//\n// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)\nfunc (prog *Program) RuntimeTypes() []types.Type {\n\tprog.methodsMu.Lock()\n\tdefer prog.methodsMu.Unlock()\n\n\tvar res []types.Type\n\tprog.methodSets.Iterate(func(T types.Type, v *methodSet) {\n\t\tif v.complete {\n\t\t\tres = append(res, T)\n\t\t}\n\t})\n\treturn res\n}\n\n// declaredFunc returns the concrete function/method denoted by obj.\n// Panic ensues if there is none.\nfunc (prog *Program) declaredFunc(obj *types.Func) *Function {\n\tif origin := obj.Origin(); origin != obj {\n\t\t// Calling method on instantiated type, create a wrapper that calls the generic type's method\n\t\tbase := prog.packageLevelValue(origin)\n\t\treturn makeInstance(prog, base.(*Function), obj.Type().(*types.Signature), nil)\n\t} else {\n\t\tif v := prog.packageLevelValue(obj); v != nil {\n\t\t\treturn v.(*Function)\n\t\t}\n\t}\n\tpanic(\"no concrete method: \" + obj.String())\n}\n\n// needMethodsOf ensures that runtime type information (including the\n// complete method set) is available for the specified type T and all\n// its subcomponents.\n//\n// needMethodsOf must be called for at least every type that is an\n// operand of some MakeInterface instruction, and for the type of\n// every exported package member.\n//\n// Precondition: T is not a method signature (*Signature with Recv()!=nil).\n//\n// Thread-safe.  (Called via emitConv from multiple builder goroutines.)\n//\n// TODO(adonovan): make this faster.  It accounts for 20% of SSA build time.\n//\n// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)\nfunc (prog *Program) needMethodsOf(T types.Type) {\n\tprog.methodsMu.Lock()\n\tprog.needMethods(T, false)\n\tprog.methodsMu.Unlock()\n}\n\n// Precondition: T is not a method signature (*Signature with Recv()!=nil).\n// Recursive case: skip => don't create methods for T.\n//\n// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)\nfunc (prog *Program) needMethods(T types.Type, skip bool) {\n\t// Each package maintains its own set of types it has visited.\n\tif prevSkip, ok := prog.runtimeTypes.At(T); ok {\n\t\t// needMethods(T) was previously called\n\t\tif !prevSkip || skip {\n\t\t\treturn // already seen, with same or false 'skip' value\n\t\t}\n\t}\n\tprog.runtimeTypes.Set(T, skip)\n\n\ttmset := prog.MethodSets.MethodSet(T)\n\n\tif !skip && !types.IsInterface(T) && tmset.Len() > 0 {\n\t\t// Create methods of T.\n\t\tmset := prog.createMethodSet(T)\n\t\tif !mset.complete {\n\t\t\tmset.complete = true\n\t\t\tn := tmset.Len()\n\t\t\tfor i := range n {\n\t\t\t\tprog.addMethod(mset, tmset.At(i))\n\t\t\t}\n\t\t}\n\t}\n\n\t// Recursion over signatures of each method.\n\tfor method := range tmset.Methods() {\n\t\tsig := method.Type().(*types.Signature)\n\t\tprog.needMethods(sig.Params(), false)\n\t\tprog.needMethods(sig.Results(), false)\n\t}\n\n\tswitch t := T.(type) {\n\tcase *types.Basic:\n\t\t// nop\n\n\tcase *types.Interface, *types.TypeParam:\n\t\t// nop---handled by recursion over method set.\n\n\tcase *types.Pointer:\n\t\tprog.needMethods(t.Elem(), false)\n\n\tcase *types.Slice:\n\t\tprog.needMethods(t.Elem(), false)\n\n\tcase *types.Chan:\n\t\tprog.needMethods(t.Elem(), false)\n\n\tcase *types.Map:\n\t\tprog.needMethods(t.Key(), false)\n\t\tprog.needMethods(t.Elem(), false)\n\n\tcase *types.Signature:\n\t\tif t.Recv() != nil {\n\t\t\tpanic(fmt.Sprintf(\"Signature %s has Recv %s\", t, t.Recv()))\n\t\t}\n\t\tprog.needMethods(t.Params(), false)\n\t\tprog.needMethods(t.Results(), false)\n\n\tcase *types.Named:\n\t\t// A pointer-to-named type can be derived from a named\n\t\t// type via reflection.  It may have methods too.\n\t\tprog.needMethods(types.NewPointer(t), false)\n\n\t\t// Consider 'type T struct{S}' where S has methods.\n\t\t// Reflection provides no way to get from T to struct{S},\n\t\t// only to S, so the method set of struct{S} is unwanted,\n\t\t// so set 'skip' flag during recursion.\n\t\tprog.needMethods(t.Underlying(), true)\n\n\tcase *types.Array:\n\t\tprog.needMethods(t.Elem(), false)\n\n\tcase *types.Struct:\n\t\tfor i, n := 0, t.NumFields(); i < n; i++ {\n\t\t\tprog.needMethods(t.Field(i).Type(), false)\n\t\t}\n\n\tcase *types.Tuple:\n\t\tfor i, n := 0, t.Len(); i < n; i++ {\n\t\t\tprog.needMethods(t.At(i).Type(), false)\n\t\t}\n\n\tcase *types.Alias:\n\t\tprog.needMethods(types.Unalias(t), false)\n\n\tdefault:\n\t\tlint.ExhaustiveTypeSwitch(T)\n\t}\n}\n"
  },
  {
    "path": "go/ir/mode.go",
    "content": "// Copyright 2015 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file defines the BuilderMode type and its command-line flag.\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n)\n\n// BuilderMode is a bitmask of options for diagnostics and checking.\n//\n// *BuilderMode satisfies the flag.Value interface.  Example:\n//\n//\tvar mode = ir.BuilderMode(0)\n//\tfunc init() { flag.Var(&mode, \"build\", ir.BuilderModeDoc) }\ntype BuilderMode uint\n\nconst (\n\tPrintPackages            BuilderMode = 1 << iota // Print package inventory to stdout\n\tPrintFunctions                                   // Print function IR code to stdout\n\tPrintSource                                      // Print source code when printing function IR\n\tLogSource                                        // Log source locations as IR builder progresses\n\tSanityCheckFunctions                             // Perform sanity checking of function bodies\n\tNaiveForm                                        // Build naïve IR form: don't replace local loads/stores with registers\n\tGlobalDebug                                      // Enable debug info for all packages\n\tSplitAfterNewInformation                         // Split live range after we learn something new about a value\n)\n\nconst BuilderModeDoc = `Options controlling the IR builder.\nThe value is a sequence of zero or more of these symbols:\nC\tperform sanity [C]hecking of the IR form.\nD\tinclude [D]ebug info for every function.\nP\tprint [P]ackage inventory.\nF\tprint [F]unction IR code.\nA\tprint [A]ST nodes responsible for IR instructions\nS\tlog [S]ource locations as IR builder progresses.\nN\tbuild [N]aive IR form: don't replace local loads/stores with registers.\nI\tSplit live range after a value is used as slice or array index\n`\n\nfunc (m BuilderMode) String() string {\n\tvar buf bytes.Buffer\n\tif m&GlobalDebug != 0 {\n\t\tbuf.WriteByte('D')\n\t}\n\tif m&PrintPackages != 0 {\n\t\tbuf.WriteByte('P')\n\t}\n\tif m&PrintFunctions != 0 {\n\t\tbuf.WriteByte('F')\n\t}\n\tif m&PrintSource != 0 {\n\t\tbuf.WriteByte('A')\n\t}\n\tif m&LogSource != 0 {\n\t\tbuf.WriteByte('S')\n\t}\n\tif m&SanityCheckFunctions != 0 {\n\t\tbuf.WriteByte('C')\n\t}\n\tif m&NaiveForm != 0 {\n\t\tbuf.WriteByte('N')\n\t}\n\tif m&SplitAfterNewInformation != 0 {\n\t\tbuf.WriteByte('I')\n\t}\n\treturn buf.String()\n}\n\n// Set parses the flag characters in s and updates *m.\nfunc (m *BuilderMode) Set(s string) error {\n\tvar mode BuilderMode\n\tfor _, c := range s {\n\t\tswitch c {\n\t\tcase 'D':\n\t\t\tmode |= GlobalDebug\n\t\tcase 'P':\n\t\t\tmode |= PrintPackages\n\t\tcase 'F':\n\t\t\tmode |= PrintFunctions\n\t\tcase 'A':\n\t\t\tmode |= PrintSource\n\t\tcase 'S':\n\t\t\tmode |= LogSource\n\t\tcase 'C':\n\t\t\tmode |= SanityCheckFunctions\n\t\tcase 'N':\n\t\t\tmode |= NaiveForm\n\t\tcase 'I':\n\t\t\tmode |= SplitAfterNewInformation\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"unknown BuilderMode option: %q\", c)\n\t\t}\n\t}\n\t*m = mode\n\treturn nil\n}\n\n// Get returns m.\nfunc (m BuilderMode) Get() any { return m }\n"
  },
  {
    "path": "go/ir/print.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file implements the String() methods for all Value and\n// Instruction types.\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/types\"\n\t\"io\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/go/types/typeutil\"\n)\n\n// relName returns the name of v relative to i.\n// In most cases, this is identical to v.Name(), but references to\n// Functions (including methods) and Globals use RelString and\n// all types are displayed with relType, so that only cross-package\n// references are package-qualified.\nfunc relName(v Value, i Instruction) string {\n\tif v == nil {\n\t\treturn \"<nil>\"\n\t}\n\tvar from *types.Package\n\tif i != nil {\n\t\tfrom = i.Parent().pkg()\n\t}\n\tswitch v := v.(type) {\n\tcase Member: // *Function or *Global\n\t\treturn v.RelString(from)\n\t}\n\treturn v.Name()\n}\n\nfunc relType(t types.Type, from *types.Package) string {\n\treturn types.TypeString(t, types.RelativeTo(from))\n}\n\nfunc relTerm(term *types.Term, from *types.Package) string {\n\ts := relType(term.Type(), from)\n\tif term.Tilde() {\n\t\treturn \"~\" + s\n\t}\n\treturn s\n}\n\nfunc relString(m Member, from *types.Package) string {\n\t// NB: not all globals have an Object (e.g. init$guard),\n\t// so use Package().Object not Object.Package().\n\tif pkg := m.Package().Pkg; pkg != nil && pkg != from {\n\t\treturn fmt.Sprintf(\"%s.%s\", pkg.Path(), m.Name())\n\t}\n\treturn m.Name()\n}\n\n// Value.String()\n//\n// This method is provided only for debugging.\n// It never appears in disassembly, which uses Value.Name().\n\nfunc (v *Parameter) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"Parameter <%s> {%s}\", relType(v.Type(), from), v.name)\n}\n\nfunc (v *FreeVar) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"FreeVar <%s> %s\", relType(v.Type(), from), v.Name())\n}\n\nfunc (v *Builtin) String() string {\n\treturn fmt.Sprintf(\"Builtin %s\", v.Name())\n}\n\n// Instruction.String()\n\nfunc (v *Alloc) String() string {\n\tfrom := v.Parent().pkg()\n\tstorage := \"Stack\"\n\tif v.Heap {\n\t\tstorage = \"Heap\"\n\t}\n\treturn fmt.Sprintf(\"%sAlloc <%s>\", storage, relType(v.Type(), from))\n}\n\nfunc (v *Sigma) String() string {\n\tfrom := v.Parent().pkg()\n\ts := fmt.Sprintf(\"Sigma <%s> [b%d] %s\", relType(v.Type(), from), v.From.Index, v.X.Name())\n\treturn s\n}\n\nfunc (v *Phi) String() string {\n\tvar b bytes.Buffer\n\tfmt.Fprintf(&b, \"Phi <%s>\", v.Type())\n\tfor i, edge := range v.Edges {\n\t\tb.WriteString(\" \")\n\t\t// Be robust against malformed CFG.\n\t\tif v.block == nil {\n\t\t\tb.WriteString(\"??\")\n\t\t\tcontinue\n\t\t}\n\t\tblock := -1\n\t\tif i < len(v.block.Preds) {\n\t\t\tblock = v.block.Preds[i].Index\n\t\t}\n\t\tfmt.Fprintf(&b, \"%d:\", block)\n\t\tedgeVal := \"<nil>\" // be robust\n\t\tif edge != nil {\n\t\t\tedgeVal = relName(edge, v)\n\t\t}\n\t\tb.WriteString(edgeVal)\n\t}\n\treturn b.String()\n}\n\nfunc printCall(v *CallCommon, prefix string, instr Instruction) string {\n\tvar b bytes.Buffer\n\tif !v.IsInvoke() {\n\t\tif value, ok := instr.(Value); ok {\n\t\t\tfmt.Fprintf(&b, \"%s <%s> %s\", prefix, relType(value.Type(), instr.Parent().pkg()), relName(v.Value, instr))\n\t\t} else {\n\t\t\tfmt.Fprintf(&b, \"%s %s\", prefix, relName(v.Value, instr))\n\t\t}\n\t} else {\n\t\tif value, ok := instr.(Value); ok {\n\t\t\tfmt.Fprintf(&b, \"%sInvoke <%s> %s.%s\", prefix, relType(value.Type(), instr.Parent().pkg()), relName(v.Value, instr), v.Method.Name())\n\t\t} else {\n\t\t\tfmt.Fprintf(&b, \"%sInvoke %s.%s\", prefix, relName(v.Value, instr), v.Method.Name())\n\t\t}\n\t}\n\tfor _, arg := range v.TypeArgs {\n\t\tb.WriteString(\" \")\n\t\tb.WriteString(relType(arg, instr.Parent().pkg()))\n\t}\n\tfor _, arg := range v.Args {\n\t\tb.WriteString(\" \")\n\t\tb.WriteString(relName(arg, instr))\n\t}\n\treturn b.String()\n}\n\nfunc (c *CallCommon) String() string {\n\treturn printCall(c, \"\", nil)\n}\n\nfunc (v *Call) String() string {\n\treturn printCall(&v.Call, \"Call\", v)\n}\n\nfunc (v *BinOp) String() string {\n\treturn fmt.Sprintf(\"BinOp <%s> {%s} %s %s\", relType(v.Type(), v.Parent().pkg()), v.Op.String(), relName(v.X, v), relName(v.Y, v))\n}\n\nfunc (v *UnOp) String() string {\n\treturn fmt.Sprintf(\"UnOp <%s> {%s} %s\", relType(v.Type(), v.Parent().pkg()), v.Op.String(), relName(v.X, v))\n}\n\nfunc (v *Load) String() string {\n\treturn fmt.Sprintf(\"Load <%s> %s\", relType(v.Type(), v.Parent().pkg()), relName(v.X, v))\n}\n\nfunc (v *Copy) String() string {\n\treturn fmt.Sprintf(\"Copy <%s> %s\", relType(v.Type(), v.Parent().pkg()), relName(v.X, v))\n}\n\nfunc printConv(prefix string, v, x Value) string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"%s <%s> %s\",\n\t\tprefix,\n\t\trelType(v.Type(), from),\n\t\trelName(x, v.(Instruction)))\n}\n\nfunc (v *ChangeType) String() string          { return printConv(\"ChangeType\", v, v.X) }\nfunc (v *Convert) String() string             { return printConv(\"Convert\", v, v.X) }\nfunc (v *ChangeInterface) String() string     { return printConv(\"ChangeInterface\", v, v.X) }\nfunc (v *SliceToArrayPointer) String() string { return printConv(\"SliceToArrayPointer\", v, v.X) }\nfunc (v *SliceToArray) String() string        { return printConv(\"SliceToArray\", v, v.X) }\nfunc (v *MakeInterface) String() string       { return printConv(\"MakeInterface\", v, v.X) }\n\nfunc (v *MakeClosure) String() string {\n\tfrom := v.Parent().pkg()\n\tvar b bytes.Buffer\n\tfmt.Fprintf(&b, \"MakeClosure <%s> %s\", relType(v.Type(), from), relName(v.Fn, v))\n\tif v.Bindings != nil {\n\t\tfor _, c := range v.Bindings {\n\t\t\tb.WriteString(\" \")\n\t\t\tb.WriteString(relName(c, v))\n\t\t}\n\t}\n\treturn b.String()\n}\n\nfunc (v *MakeSlice) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"MakeSlice <%s> %s %s\",\n\t\trelType(v.Type(), from),\n\t\trelName(v.Len, v),\n\t\trelName(v.Cap, v))\n}\n\nfunc (v *Slice) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"Slice <%s> %s %s %s %s\",\n\t\trelType(v.Type(), from), relName(v.X, v), relName(v.Low, v), relName(v.High, v), relName(v.Max, v))\n}\n\nfunc (v *MakeMap) String() string {\n\tres := \"\"\n\tif v.Reserve != nil {\n\t\tres = relName(v.Reserve, v)\n\t}\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"MakeMap <%s> %s\", relType(v.Type(), from), res)\n}\n\nfunc (v *MakeChan) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"MakeChan <%s> %s\", relType(v.Type(), from), relName(v.Size, v))\n}\n\nfunc (v *FieldAddr) String() string {\n\tfrom := v.Parent().pkg()\n\t// v.X.Type() might be a pointer to a type parameter whose core type is a pointer to a struct\n\tst := deref(typeutil.CoreType(deref(v.X.Type()))).Underlying().(*types.Struct)\n\t// Be robust against a bad index.\n\tname := \"?\"\n\tif 0 <= v.Field && v.Field < st.NumFields() {\n\t\tname = st.Field(v.Field).Name()\n\t}\n\treturn fmt.Sprintf(\"FieldAddr <%s> [%d] (%s) %s\", relType(v.Type(), from), v.Field, name, relName(v.X, v))\n}\n\nfunc (v *Field) String() string {\n\tst := typeutil.CoreType(v.X.Type()).Underlying().(*types.Struct)\n\t// Be robust against a bad index.\n\tname := \"?\"\n\tif 0 <= v.Field && v.Field < st.NumFields() {\n\t\tname = st.Field(v.Field).Name()\n\t}\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"Field <%s> [%d] (%s) %s\", relType(v.Type(), from), v.Field, name, relName(v.X, v))\n}\n\nfunc (v *IndexAddr) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"IndexAddr <%s> %s %s\", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v))\n}\n\nfunc (v *Index) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"Index <%s> %s %s\", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v))\n}\n\nfunc (v *MapLookup) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"MapLookup <%s> %s %s\", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v))\n}\n\nfunc (v *StringLookup) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"StringLookup <%s> %s %s\", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v))\n}\n\nfunc (v *Range) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"Range <%s> %s\", relType(v.Type(), from), relName(v.X, v))\n}\n\nfunc (v *Next) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"Next <%s> %s\", relType(v.Type(), from), relName(v.Iter, v))\n}\n\nfunc (v *TypeAssert) String() string {\n\tfrom := v.Parent().pkg()\n\treturn fmt.Sprintf(\"TypeAssert <%s> %s\", relType(v.Type(), from), relName(v.X, v))\n}\n\nfunc (v *Extract) String() string {\n\tfrom := v.Parent().pkg()\n\tname := v.Tuple.Type().(*types.Tuple).At(v.Index).Name()\n\treturn fmt.Sprintf(\"Extract <%s> [%d] (%s) %s\", relType(v.Type(), from), v.Index, name, relName(v.Tuple, v))\n}\n\nfunc (s *Jump) String() string {\n\t// Be robust against malformed CFG.\n\tblock := -1\n\tif s.block != nil && len(s.block.Succs) == 1 {\n\t\tblock = s.block.Succs[0].Index\n\t}\n\tstr := fmt.Sprintf(\"Jump → b%d\", block)\n\tif s.Comment() != \"\" {\n\t\tstr = fmt.Sprintf(\"%s # %s\", str, s.Comment())\n\t}\n\treturn str\n}\n\nfunc (s *Unreachable) String() string {\n\t// Be robust against malformed CFG.\n\tblock := -1\n\tif s.block != nil && len(s.block.Succs) == 1 {\n\t\tblock = s.block.Succs[0].Index\n\t}\n\treturn fmt.Sprintf(\"Unreachable → b%d\", block)\n}\n\nfunc (s *If) String() string {\n\t// Be robust against malformed CFG.\n\ttblock, fblock := -1, -1\n\tif s.block != nil && len(s.block.Succs) == 2 {\n\t\ttblock = s.block.Succs[0].Index\n\t\tfblock = s.block.Succs[1].Index\n\t}\n\treturn fmt.Sprintf(\"If %s → b%d b%d\", relName(s.Cond, s), tblock, fblock)\n}\n\nfunc (s *ConstantSwitch) String() string {\n\tvar b bytes.Buffer\n\tfmt.Fprintf(&b, \"ConstantSwitch %s\", relName(s.Tag, s))\n\tfor _, cond := range s.Conds {\n\t\tfmt.Fprintf(&b, \" %s\", relName(cond, s))\n\t}\n\tfmt.Fprint(&b, \" →\")\n\tfor _, succ := range s.block.Succs {\n\t\tfmt.Fprintf(&b, \" b%d\", succ.Index)\n\t}\n\treturn b.String()\n}\n\nfunc (v *CompositeValue) String() string {\n\tvar b bytes.Buffer\n\tfrom := v.Parent().pkg()\n\tfmt.Fprintf(&b, \"CompositeValue <%s>\", relType(v.Type(), from))\n\tif v.NumSet >= len(v.Values) {\n\t\t// All values provided\n\t\tfmt.Fprint(&b, \" [all]\")\n\t} else if v.Bitmap.BitLen() == 0 {\n\t\t// No values provided\n\t\tfmt.Fprint(&b, \" [none]\")\n\t} else {\n\t\t// Some values provided\n\t\tbits := fmt.Appendf(nil, \"%0*b\", len(v.Values), &v.Bitmap)\n\t\tfor i := 0; i < len(bits)/2; i++ {\n\t\t\to := len(bits) - 1 - i\n\t\t\tbits[i], bits[o] = bits[o], bits[i]\n\t\t}\n\t\tfmt.Fprintf(&b, \" [%s]\", bits)\n\t}\n\tfor _, vv := range v.Values {\n\t\tfmt.Fprintf(&b, \" %s\", relName(vv, v))\n\t}\n\treturn b.String()\n}\n\nfunc (s *TypeSwitch) String() string {\n\tfrom := s.Parent().pkg()\n\tvar b bytes.Buffer\n\tfmt.Fprintf(&b, \"TypeSwitch <%s> %s\", relType(s.typ, from), relName(s.Tag, s))\n\tfor _, cond := range s.Conds {\n\t\tfmt.Fprintf(&b, \" %q\", relType(cond, s.block.parent.pkg()))\n\t}\n\treturn b.String()\n}\n\nfunc (s *Go) String() string {\n\treturn printCall(&s.Call, \"Go\", s)\n}\n\nfunc (s *Panic) String() string {\n\t// Be robust against malformed CFG.\n\tblock := -1\n\tif s.block != nil && len(s.block.Succs) == 1 {\n\t\tblock = s.block.Succs[0].Index\n\t}\n\treturn fmt.Sprintf(\"Panic %s → b%d\", relName(s.X, s), block)\n}\n\nfunc (s *Return) String() string {\n\tvar b bytes.Buffer\n\tb.WriteString(\"Return\")\n\tfor _, r := range s.Results {\n\t\tb.WriteString(\" \")\n\t\tb.WriteString(relName(r, s))\n\t}\n\treturn b.String()\n}\n\nfunc (*RunDefers) String() string {\n\treturn \"RunDefers\"\n}\n\nfunc (s *Send) String() string {\n\treturn fmt.Sprintf(\"Send %s %s\", relName(s.Chan, s), relName(s.X, s))\n}\n\nfunc (recv *Recv) String() string {\n\tfrom := recv.Parent().pkg()\n\treturn fmt.Sprintf(\"Recv <%s> %s\", relType(recv.Type(), from), relName(recv.Chan, recv))\n}\n\nfunc (s *Defer) String() string {\n\tprefix := \"Defer \"\n\tif s._DeferStack != nil {\n\t\tprefix += \"[\" + relName(s._DeferStack, s) + \"] \"\n\t}\n\tc := printCall(&s.Call, prefix, s)\n\treturn c\n}\n\nfunc (s *Select) String() string {\n\tvar b bytes.Buffer\n\tfor i, st := range s.States {\n\t\tif i > 0 {\n\t\t\tb.WriteString(\", \")\n\t\t}\n\t\tif st.Dir == types.RecvOnly {\n\t\t\tb.WriteString(\"<-\")\n\t\t\tb.WriteString(relName(st.Chan, s))\n\t\t} else {\n\t\t\tb.WriteString(relName(st.Chan, s))\n\t\t\tb.WriteString(\"<-\")\n\t\t\tb.WriteString(relName(st.Send, s))\n\t\t}\n\t}\n\tnon := \"\"\n\tif !s.Blocking {\n\t\tnon = \"Non\"\n\t}\n\tfrom := s.Parent().pkg()\n\treturn fmt.Sprintf(\"Select%sBlocking <%s> [%s]\", non, relType(s.Type(), from), b.String())\n}\n\nfunc (s *Store) String() string {\n\treturn fmt.Sprintf(\"Store {%s} %s %s\",\n\t\ts.Val.Type(), relName(s.Addr, s), relName(s.Val, s))\n}\n\nfunc (s *BlankStore) String() string {\n\treturn fmt.Sprintf(\"BlankStore %s\", relName(s.Val, s))\n}\n\nfunc (s *MapUpdate) String() string {\n\treturn fmt.Sprintf(\"MapUpdate %s %s %s\", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s))\n}\n\nfunc (s *DebugRef) String() string {\n\tp := s.Parent().Prog.Fset.Position(s.Pos())\n\tvar descr any\n\tif s.object != nil {\n\t\tdescr = s.object // e.g. \"var x int\"\n\t} else {\n\t\tdescr = reflect.TypeOf(s.Expr) // e.g. \"*ast.CallExpr\"\n\t}\n\tvar addr string\n\tif s.IsAddr {\n\t\taddr = \"address of \"\n\t}\n\treturn fmt.Sprintf(\"; %s%s @ %d:%d is %s\", addr, descr, p.Line, p.Column, s.X.Name())\n}\n\nfunc (p *Package) String() string {\n\treturn \"package \" + p.Pkg.Path()\n}\n\nvar _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer\n\nfunc (p *Package) WriteTo(w io.Writer) (int64, error) {\n\tvar buf bytes.Buffer\n\tWritePackage(&buf, p)\n\tn, err := w.Write(buf.Bytes())\n\treturn int64(n), err\n}\n\n// WritePackage writes to buf a human-readable summary of p.\nfunc WritePackage(buf *bytes.Buffer, p *Package) {\n\tfmt.Fprintf(buf, \"%s:\\n\", p)\n\n\tvar names []string\n\tmaxname := 0\n\tfor name := range p.Members {\n\t\tif l := len(name); l > maxname {\n\t\t\tmaxname = l\n\t\t}\n\t\tnames = append(names, name)\n\t}\n\n\tfrom := p.Pkg\n\tsort.Strings(names)\n\tfor _, name := range names {\n\t\tswitch mem := p.Members[name].(type) {\n\t\tcase *NamedConst:\n\t\t\tfmt.Fprintf(buf, \"  const %-*s %s = %s\\n\",\n\t\t\t\tmaxname, name, mem.Name(), mem.Value.RelString(from))\n\n\t\tcase *Function:\n\t\t\tfmt.Fprintf(buf, \"  func  %-*s %s\\n\",\n\t\t\t\tmaxname, name, relType(mem.Type(), from))\n\n\t\tcase *Type:\n\t\t\tfmt.Fprintf(buf, \"  type  %-*s %s\\n\",\n\t\t\t\tmaxname, name, relType(mem.Type().Underlying(), from))\n\t\t\tfor _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {\n\t\t\t\tfmt.Fprintf(buf, \"    %s\\n\", types.SelectionString(meth, types.RelativeTo(from)))\n\t\t\t}\n\n\t\tcase *Global:\n\t\t\tfmt.Fprintf(buf, \"  var   %-*s %s\\n\",\n\t\t\t\tmaxname, name, relType(mem.Type().(*types.Pointer).Elem(), from))\n\t\t}\n\t}\n\n\tfmt.Fprintf(buf, \"\\n\")\n}\n\nfunc (v *MultiConvert) String() string {\n\tfrom := v.Parent().Pkg.Pkg\n\n\tvar b strings.Builder\n\tb.WriteString(printConv(\"MultiConvert\", v, v.X))\n\tb.WriteString(\" [\")\n\tfor i, s := range v.from.Terms {\n\t\tfor j, d := range v.to.Terms {\n\t\t\tif i != 0 || j != 0 {\n\t\t\t\tb.WriteString(\" | \")\n\t\t\t}\n\t\t\tfmt.Fprintf(&b, \"%s -> %s\", relTerm(s, from), relTerm(d, from))\n\t\t}\n\t}\n\tb.WriteString(\"]\")\n\treturn b.String()\n}\n"
  },
  {
    "path": "go/ir/sanity.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// An optional pass for sanity-checking invariants of the IR representation.\n// Currently it checks CFG invariants but little at the instruction level.\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\t\"io\"\n\t\"os\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/go/types/typeutil\"\n)\n\ntype sanity struct {\n\treporter io.Writer\n\tfn       *Function\n\tblock    *BasicBlock\n\tinstrs   map[Instruction]struct{}\n\tinsane   bool\n}\n\n// sanityCheck performs integrity checking of the IR representation\n// of the function fn and returns true if it was valid.  Diagnostics\n// are written to reporter if non-nil, os.Stderr otherwise.  Some\n// diagnostics are only warnings and do not imply a negative result.\n//\n// Sanity-checking is intended to facilitate the debugging of code\n// transformation passes.\nfunc sanityCheck(fn *Function, reporter io.Writer) bool {\n\tif reporter == nil {\n\t\treporter = os.Stderr\n\t}\n\treturn (&sanity{reporter: reporter}).checkFunction(fn)\n}\n\n// mustSanityCheck is like sanityCheck but panics instead of returning\n// a negative result.\nfunc mustSanityCheck(fn *Function, reporter io.Writer) {\n\tif !sanityCheck(fn, reporter) {\n\t\tfn.WriteTo(os.Stderr)\n\t\tpanic(\"SanityCheck failed\")\n\t}\n}\n\nfunc (s *sanity) diagnostic(prefix, format string, args ...any) {\n\tfmt.Fprintf(s.reporter, \"%s: function %s\", prefix, s.fn)\n\tif s.block != nil {\n\t\tfmt.Fprintf(s.reporter, \", block %s\", s.block)\n\t}\n\tio.WriteString(s.reporter, \": \")\n\tfmt.Fprintf(s.reporter, format, args...)\n\tio.WriteString(s.reporter, \"\\n\")\n}\n\nfunc (s *sanity) errorf(format string, args ...any) {\n\ts.insane = true\n\ts.diagnostic(\"Error\", format, args...)\n}\n\nfunc (s *sanity) warnf(format string, args ...any) {\n\ts.diagnostic(\"Warning\", format, args...)\n}\n\n// findDuplicate returns an arbitrary basic block that appeared more\n// than once in blocks, or nil if all were unique.\nfunc findDuplicate(blocks []*BasicBlock) *BasicBlock {\n\tif len(blocks) < 2 {\n\t\treturn nil\n\t}\n\tif blocks[0] == blocks[1] {\n\t\treturn blocks[0]\n\t}\n\t// Slow path:\n\tm := make(map[*BasicBlock]bool)\n\tfor _, b := range blocks {\n\t\tif m[b] {\n\t\t\treturn b\n\t\t}\n\t\tm[b] = true\n\t}\n\treturn nil\n}\n\nfunc (s *sanity) checkInstr(idx int, instr Instruction) {\n\tswitch instr := instr.(type) {\n\tcase *If, *Jump, *Return, *Panic, *Unreachable, *ConstantSwitch:\n\t\ts.errorf(\"control flow instruction not at end of block\")\n\tcase *Sigma:\n\t\tif idx > 0 {\n\t\t\tprev := s.block.Instrs[idx-1]\n\t\t\tif _, ok := prev.(*Sigma); !ok {\n\t\t\t\ts.errorf(\"Sigma instruction follows a non-Sigma: %T\", prev)\n\t\t\t}\n\t\t}\n\tcase *Phi:\n\t\tif idx == 0 {\n\t\t\t// It suffices to apply this check to just the first phi node.\n\t\t\tif dup := findDuplicate(s.block.Preds); dup != nil {\n\t\t\t\ts.errorf(\"phi node in block with duplicate predecessor %s\", dup)\n\t\t\t}\n\t\t} else {\n\t\t\tprev := s.block.Instrs[idx-1]\n\t\t\tswitch prev.(type) {\n\t\t\tcase *Phi, *Sigma:\n\t\t\tdefault:\n\t\t\t\ts.errorf(\"Phi instruction follows a non-Phi, non-Sigma: %T\", prev)\n\t\t\t}\n\t\t}\n\t\tif ne, np := len(instr.Edges), len(s.block.Preds); ne != np {\n\t\t\ts.errorf(\"phi node has %d edges but %d predecessors\", ne, np)\n\n\t\t} else {\n\t\t\tfor i, e := range instr.Edges {\n\t\t\t\tif e == nil {\n\t\t\t\t\ts.errorf(\"phi node '%s' has no value for edge #%d from %s\", instr.Comment(), i, s.block.Preds[i])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tcase *Alloc:\n\t\tif !instr.Heap && !slices.Contains(s.fn.Locals, instr) {\n\t\t\ts.errorf(\"local alloc %s = %s does not appear in Function.Locals\", instr.Name(), instr)\n\t\t}\n\n\tcase *BinOp:\n\tcase *Call:\n\tcase *ChangeInterface:\n\tcase *ChangeType:\n\tcase *SliceToArrayPointer:\n\tcase *SliceToArray:\n\tcase *Convert:\n\t\ttsetInstrX := typeutil.NewTypeSet(instr.X.Type().Underlying())\n\t\ttsetInstr := typeutil.NewTypeSet(instr.Type().Underlying())\n\t\tok1 := tsetInstr.Any(func(term *types.Term) bool { _, ok := term.Type().Underlying().(*types.Basic); return ok })\n\t\tok2 := tsetInstrX.Any(func(term *types.Term) bool { _, ok := term.Type().Underlying().(*types.Basic); return ok })\n\t\tif !ok1 && !ok2 {\n\t\t\ts.errorf(\"convert %s -> %s: at least one type set must contain basic type\", instr.X.Type(), instr.Type())\n\t\t}\n\n\tcase *MultiConvert:\n\tcase *Defer:\n\tcase *Extract:\n\tcase *Field:\n\tcase *FieldAddr:\n\tcase *Go:\n\tcase *Index:\n\tcase *IndexAddr:\n\tcase *MapLookup:\n\tcase *StringLookup:\n\tcase *MakeChan:\n\tcase *MakeClosure:\n\t\tnumFree := len(instr.Fn.(*Function).FreeVars)\n\t\tnumBind := len(instr.Bindings)\n\t\tif numFree != numBind {\n\t\t\ts.errorf(\"MakeClosure has %d Bindings for function %s with %d free vars\",\n\t\t\t\tnumBind, instr.Fn, numFree)\n\n\t\t}\n\t\tif recv := instr.Type().(*types.Signature).Recv(); recv != nil {\n\t\t\ts.errorf(\"MakeClosure's type includes receiver %s\", recv.Type())\n\t\t}\n\n\tcase *MakeInterface:\n\tcase *MakeMap:\n\tcase *MakeSlice:\n\tcase *MapUpdate:\n\tcase *Next:\n\tcase *Range:\n\tcase *RunDefers:\n\tcase *Select:\n\tcase *Send:\n\tcase *Slice:\n\tcase *Store:\n\tcase *TypeAssert:\n\tcase *UnOp:\n\tcase *DebugRef:\n\tcase *BlankStore:\n\tcase *Load:\n\tcase *Parameter:\n\tcase *Const:\n\tcase *AggregateConst:\n\tcase *ArrayConst:\n\tcase *GenericConst:\n\tcase *Recv:\n\tcase *TypeSwitch:\n\tcase *CompositeValue:\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"Unknown instruction type: %T\", instr))\n\t}\n\n\tif call, ok := instr.(CallInstruction); ok {\n\t\tif call.Common().Signature() == nil {\n\t\t\ts.errorf(\"nil signature: %s\", call)\n\t\t}\n\t}\n\n\t// Check that value-defining instructions have valid types\n\t// and a valid referrer list.\n\tif v, ok := instr.(Value); ok {\n\t\tt := v.Type()\n\t\tif t == nil {\n\t\t\ts.errorf(\"no type: %s = %s\", v.Name(), v)\n\t\t} else if b, ok := t.Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {\n\t\t\tif _, ok := v.(*Const); !ok {\n\t\t\t\ts.errorf(\"instruction has 'untyped' result: %s = %s : %s\", v.Name(), v, t)\n\t\t\t}\n\t\t}\n\t\ts.checkReferrerList(v)\n\t}\n\n\t// Untyped constants are legal as instruction Operands(),\n\t// for example:\n\t//   _ = \"foo\"[0]\n\t// or:\n\t//   if wordsize==64 {...}\n\n\t// All other non-Instruction Values can be found via their\n\t// enclosing Function or Package.\n}\n\nfunc (s *sanity) checkFinalInstr(instr Instruction) {\n\tswitch instr := instr.(type) {\n\tcase *If:\n\t\tif nsuccs := len(s.block.Succs); nsuccs != 2 {\n\t\t\ts.errorf(\"If-terminated block has %d successors; expected 2\", nsuccs)\n\t\t\treturn\n\t\t}\n\t\tif s.block.Succs[0] == s.block.Succs[1] {\n\t\t\ts.errorf(\"If-instruction has same True, False target blocks: %s\", s.block.Succs[0])\n\t\t\treturn\n\t\t}\n\n\tcase *Jump:\n\t\tif nsuccs := len(s.block.Succs); nsuccs != 1 {\n\t\t\ts.errorf(\"Jump-terminated block has %d successors; expected 1\", nsuccs)\n\t\t\treturn\n\t\t}\n\n\tcase *Return:\n\t\tif nsuccs := len(s.block.Succs); nsuccs != 0 {\n\t\t\ts.errorf(\"Return-terminated block has %d successors; expected none\", nsuccs)\n\t\t\treturn\n\t\t}\n\t\tif na, nf := len(instr.Results), s.fn.Signature.Results().Len(); nf != na {\n\t\t\ts.errorf(\"%d-ary return in %d-ary function\", na, nf)\n\t\t}\n\n\tcase *Panic:\n\t\tif nsuccs := len(s.block.Succs); nsuccs != 1 {\n\t\t\ts.errorf(\"Panic-terminated block has %d successors; expected one\", nsuccs)\n\t\t\treturn\n\t\t}\n\n\tcase *Unreachable:\n\t\tif nsuccs := len(s.block.Succs); nsuccs != 1 {\n\t\t\ts.errorf(\"Unreachable-terminated block has %d successors; expected one\", nsuccs)\n\t\t\treturn\n\t\t}\n\n\tcase *ConstantSwitch:\n\n\tdefault:\n\t\ts.errorf(\"non-control flow instruction at end of block\")\n\t}\n}\n\nfunc (s *sanity) checkBlock(b *BasicBlock, index int) {\n\ts.block = b\n\n\tif b.Index != index {\n\t\ts.errorf(\"block has incorrect Index %d\", b.Index)\n\t}\n\tif b.parent != s.fn {\n\t\ts.errorf(\"block has incorrect parent %s\", b.parent)\n\t}\n\n\t// Check all blocks are reachable.\n\t// (The entry block is always implicitly reachable, the exit block may be unreachable.)\n\tif index > 1 && len(b.Preds) == 0 {\n\t\ts.warnf(\"unreachable block\")\n\t\tif b.Instrs == nil {\n\t\t\t// Since this block is about to be pruned,\n\t\t\t// tolerating transient problems in it\n\t\t\t// simplifies other optimizations.\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Check predecessor and successor relations are dual,\n\t// and that all blocks in CFG belong to same function.\n\tfor _, a := range b.Preds {\n\t\tif !slices.Contains(a.Succs, b) {\n\t\t\ts.errorf(\"expected successor edge in predecessor %s; found only: %s\", a, a.Succs)\n\t\t}\n\t\tif a.parent != s.fn {\n\t\t\ts.errorf(\"predecessor %s belongs to different function %s\", a, a.parent)\n\t\t}\n\t}\n\tfor _, c := range b.Succs {\n\t\tif !slices.Contains(c.Preds, b) {\n\t\t\ts.errorf(\"expected predecessor edge in successor %s; found only: %s\", c, c.Preds)\n\t\t}\n\t\tif c.parent != s.fn {\n\t\t\ts.errorf(\"successor %s belongs to different function %s\", c, c.parent)\n\t\t}\n\t}\n\n\t// Check each instruction is sane.\n\tn := len(b.Instrs)\n\tif n == 0 {\n\t\ts.errorf(\"basic block contains no instructions\")\n\t}\n\tvar rands [10]*Value // reuse storage\n\tfor j, instr := range b.Instrs {\n\t\tif instr == nil {\n\t\t\ts.errorf(\"nil instruction at index %d\", j)\n\t\t\tcontinue\n\t\t}\n\t\tif b2 := instr.Block(); b2 == nil {\n\t\t\ts.errorf(\"nil Block() for instruction at index %d\", j)\n\t\t\tcontinue\n\t\t} else if b2 != b {\n\t\t\ts.errorf(\"wrong Block() (%s) for instruction at index %d \", b2, j)\n\t\t\tcontinue\n\t\t}\n\t\tif j < n-1 {\n\t\t\ts.checkInstr(j, instr)\n\t\t} else {\n\t\t\ts.checkFinalInstr(instr)\n\t\t}\n\n\t\t// Check Instruction.Operands.\n\toperands:\n\t\tfor i, op := range instr.Operands(rands[:0]) {\n\t\t\tif op == nil {\n\t\t\t\ts.errorf(\"nil operand pointer %d of %s\", i, instr)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tval := *op\n\t\t\tif val == nil {\n\t\t\t\tcontinue // a nil operand is ok\n\t\t\t}\n\n\t\t\t// Check that \"untyped\" types only appear on constant operands.\n\t\t\tif _, ok := (*op).(*Const); !ok {\n\t\t\t\tif basic, ok := types.Unalias((*op).Type()).(*types.Basic); ok {\n\t\t\t\t\tif basic.Info()&types.IsUntyped != 0 {\n\t\t\t\t\t\ts.errorf(\"operand #%d of %s is untyped: %s\", i, instr, basic)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check that Operands that are also Instructions belong to same function.\n\t\t\t// TODO(adonovan): also check their block dominates block b.\n\t\t\tif val, ok := val.(Instruction); ok {\n\t\t\t\tif val.Block() == nil {\n\t\t\t\t\ts.errorf(\"operand %d of %s is an instruction (%s) that belongs to no block\", i, instr, val)\n\t\t\t\t} else if val.Parent() != s.fn {\n\t\t\t\t\ts.errorf(\"operand %d of %s is an instruction (%s) from function %s\", i, instr, val, val.Parent())\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check that each function-local operand of\n\t\t\t// instr refers back to instr.  (NB: quadratic)\n\t\t\tswitch val := val.(type) {\n\t\t\tcase *Const, *Global, *Builtin:\n\t\t\t\tcontinue // not local\n\t\t\tcase *Function:\n\t\t\t\tif val.parent == nil {\n\t\t\t\t\tcontinue // only anon functions are local\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// TODO(adonovan): check val.Parent() != nil <=> val.Referrers() is defined.\n\n\t\t\tif refs := val.Referrers(); refs != nil {\n\t\t\t\tfor _, ref := range *refs {\n\t\t\t\t\tif ref == instr {\n\t\t\t\t\t\tcontinue operands\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ts.errorf(\"operand %d of %s (%s) does not refer to us\", i, instr, val)\n\t\t\t} else {\n\t\t\t\ts.errorf(\"operand %d of %s (%s) has no referrers\", i, instr, val)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (s *sanity) checkReferrerList(v Value) {\n\trefs := v.Referrers()\n\tif refs == nil {\n\t\ts.errorf(\"%s has missing referrer list\", v.Name())\n\t\treturn\n\t}\n\tfor i, ref := range *refs {\n\t\tif _, ok := s.instrs[ref]; !ok {\n\t\t\tif val, ok := ref.(Value); ok {\n\t\t\t\ts.errorf(\"%s.Referrers()[%d] = %s = %s is not an instruction belonging to this function\", v.Name(), i, val.Name(), val)\n\t\t\t} else {\n\t\t\t\ts.errorf(\"%s.Referrers()[%d] = %s is not an instruction belonging to this function\", v.Name(), i, ref)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (s *sanity) checkFunction(fn *Function) bool {\n\t// TODO(adonovan): check Function invariants:\n\t// - check params match signature\n\t// - check transient fields are nil\n\t// - warn if any fn.Locals do not appear among block instructions.\n\ts.fn = fn\n\tif fn.Prog == nil {\n\t\ts.errorf(\"nil Prog\")\n\t}\n\n\tvar buf bytes.Buffer\n\t_ = fn.String()            // must not crash\n\t_ = fn.RelString(fn.pkg()) // must not crash\n\tWriteFunction(&buf, fn)    // must not crash\n\n\t// All functions have a package, except delegates (which are\n\t// shared across packages, or duplicated as weak symbols in a\n\t// separate-compilation model), and error.Error.\n\tif fn.Pkg == nil {\n\t\tswitch fn.Synthetic {\n\t\tcase SyntheticWrapper, SyntheticBound, SyntheticThunk, SyntheticGeneric:\n\t\tdefault:\n\t\t\tif !strings.HasSuffix(fn.name, \"Error\") {\n\t\t\t\ts.errorf(\"nil Pkg\")\n\t\t\t}\n\t\t}\n\t}\n\tif syn, src := fn.Synthetic == 0, fn.source != nil; src != syn {\n\t\tif _, ok := fn.source.(*ast.RangeStmt); !ok || fn.Synthetic != SyntheticRangeOverFuncYield {\n\t\t\t// Only range-over-func yield functions are synthetic and have syntax\n\t\t\ts.errorf(\"got fromSource=%t, hasSyntax=%t; want same values\", src, syn)\n\t\t}\n\t}\n\tfor i, l := range fn.Locals {\n\t\tif l.Parent() != fn {\n\t\t\ts.errorf(\"Local %s at index %d has wrong parent\", l.Name(), i)\n\t\t}\n\t\tif l.Heap {\n\t\t\ts.errorf(\"Local %s at index %d has Heap flag set\", l.Name(), i)\n\t\t}\n\t}\n\t// Build the set of valid referrers.\n\ts.instrs = make(map[Instruction]struct{})\n\tfor _, b := range fn.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\ts.instrs[instr] = struct{}{}\n\t\t}\n\t}\n\tfor i, p := range fn.Params {\n\t\tif p.Parent() != fn {\n\t\t\ts.errorf(\"Param %s at index %d has wrong parent\", p.Name(), i)\n\t\t}\n\t\t// Check common suffix of Signature and Params match type.\n\t\tif sig := fn.Signature; sig != nil {\n\t\t\tj := i - len(fn.Params) + sig.Params().Len() // index within sig.Params\n\t\t\tif j < 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif !types.Identical(p.Type(), sig.Params().At(j).Type()) {\n\t\t\t\ts.errorf(\"Param %s at index %d has wrong type (%s, versus %s in Signature)\", p.Name(), i, p.Type(), sig.Params().At(j).Type())\n\n\t\t\t}\n\t\t}\n\n\t\ts.checkReferrerList(p)\n\t}\n\tfor i, fv := range fn.FreeVars {\n\t\tif fv.Parent() != fn {\n\t\t\ts.errorf(\"FreeVar %s at index %d has wrong parent\", fv.Name(), i)\n\t\t}\n\t\ts.checkReferrerList(fv)\n\t}\n\n\tif fn.Blocks != nil && len(fn.Blocks) == 0 {\n\t\t// Function _had_ blocks (so it's not external) but\n\t\t// they were \"optimized\" away, even the entry block.\n\t\ts.errorf(\"Blocks slice is non-nil but empty\")\n\t}\n\tfor i, b := range fn.Blocks {\n\t\tif b == nil {\n\t\t\ts.warnf(\"nil *BasicBlock at f.Blocks[%d]\", i)\n\t\t\tcontinue\n\t\t}\n\t\ts.checkBlock(b, i)\n\t}\n\n\ts.block = nil\n\tfor i, anon := range fn.AnonFuncs {\n\t\tif anon.Parent() != fn {\n\t\t\ts.errorf(\"AnonFuncs[%d]=%s but %s.Parent()=%s\", i, anon, anon, anon.Parent())\n\t\t}\n\t}\n\ts.fn = nil\n\treturn !s.insane\n}\n\n// sanityCheckPackage checks invariants of packages upon creation.\n// It does not require that the package is built.\n// Unlike sanityCheck (for functions), it just panics at the first error.\nfunc sanityCheckPackage(pkg *Package) {\n\tif pkg.Pkg == nil {\n\t\tpanic(fmt.Sprintf(\"Package %s has no Object\", pkg))\n\t}\n\t_ = pkg.String() // must not crash\n\n\tfor name, mem := range pkg.Members {\n\t\tif name != mem.Name() {\n\t\t\tpanic(fmt.Sprintf(\"%s: %T.Name() = %s, want %s\",\n\t\t\t\tpkg.Pkg.Path(), mem, mem.Name(), name))\n\t\t}\n\t\tobj := mem.Object()\n\t\tif obj == nil {\n\t\t\t// This check is sound because fields\n\t\t\t// {Global,Function}.object have type\n\t\t\t// types.Object.  (If they were declared as\n\t\t\t// *types.{Var,Func}, we'd have a non-empty\n\t\t\t// interface containing a nil pointer.)\n\n\t\t\tcontinue // not all members have typechecker objects\n\t\t}\n\t\tif obj.Name() != name {\n\t\t\tif obj.Name() == \"init\" && strings.HasPrefix(mem.Name(), \"init#\") {\n\t\t\t\t// Ok.  The name of a declared init function varies between\n\t\t\t\t// its types.Func (\"init\") and its ir.Function (\"init#%d\").\n\t\t\t} else {\n\t\t\t\tpanic(fmt.Sprintf(\"%s: %T.Object().Name() = %s, want %s\",\n\t\t\t\t\tpkg.Pkg.Path(), mem, obj.Name(), name))\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "go/ir/source.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file defines utilities for working with source positions\n// or source-level named entities (\"objects\").\n\n// TODO(adonovan): test that {Value,Instruction}.Pos() positions match\n// the originating syntax, as specified.\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n)\n\n// EnclosingFunction returns the function that contains the syntax\n// node denoted by path.\n//\n// Syntax associated with package-level variable specifications is\n// enclosed by the package's init() function.\n//\n// Returns nil if not found; reasons might include:\n//   - the node is not enclosed by any function.\n//   - the node is within an anonymous function (FuncLit) and\n//     its IR function has not been created yet\n//     (pkg.Build() has not yet been called).\nfunc EnclosingFunction(pkg *Package, path []ast.Node) *Function {\n\t// Start with package-level function...\n\tfn := findEnclosingPackageLevelFunction(pkg, path)\n\tif fn == nil {\n\t\treturn nil // not in any function\n\t}\n\n\t// ...then walk down the nested anonymous functions.\n\tn := len(path)\nouter:\n\tfor i := range path {\n\t\tif lit, ok := path[n-1-i].(*ast.FuncLit); ok {\n\t\t\tfor _, anon := range fn.AnonFuncs {\n\t\t\t\tif anon.Pos() == lit.Type.Func {\n\t\t\t\t\tfn = anon\n\t\t\t\t\tcontinue outer\n\t\t\t\t}\n\t\t\t}\n\t\t\t// IR function not found:\n\t\t\t// - package not yet built, or maybe\n\t\t\t// - builder skipped FuncLit in dead block\n\t\t\t//   (in principle; but currently the Builder\n\t\t\t//   generates even dead FuncLits).\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn fn\n}\n\n// HasEnclosingFunction returns true if the AST node denoted by path\n// is contained within the declaration of some function or\n// package-level variable.\n//\n// Unlike EnclosingFunction, the behaviour of this function does not\n// depend on whether IR code for pkg has been built, so it can be\n// used to quickly reject check inputs that will cause\n// EnclosingFunction to fail, prior to IR building.\nfunc HasEnclosingFunction(pkg *Package, path []ast.Node) bool {\n\treturn findEnclosingPackageLevelFunction(pkg, path) != nil\n}\n\n// findEnclosingPackageLevelFunction returns the Function\n// corresponding to the package-level function enclosing path.\nfunc findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {\n\tif n := len(path); n >= 2 { // [... {Gen,Func}Decl File]\n\t\tswitch decl := path[n-2].(type) {\n\t\tcase *ast.GenDecl:\n\t\t\tif decl.Tok == token.VAR && n >= 3 {\n\t\t\t\t// Package-level 'var' initializer.\n\t\t\t\treturn pkg.init\n\t\t\t}\n\n\t\tcase *ast.FuncDecl:\n\t\t\t// Declared function/method.\n\t\t\tfn := findNamedFunc(pkg, decl.Pos())\n\t\t\tif fn == nil && decl.Recv == nil && decl.Name.Name == \"init\" {\n\t\t\t\t// Hack: return non-nil when IR is not yet\n\t\t\t\t// built so that HasEnclosingFunction works.\n\t\t\t\treturn pkg.init\n\t\t\t}\n\t\t\treturn fn\n\t\t}\n\t}\n\treturn nil // not in any function\n}\n\n// findNamedFunc returns the named function whose FuncDecl.Ident is at\n// position pos.\nfunc findNamedFunc(pkg *Package, pos token.Pos) *Function {\n\tfor _, fn := range pkg.Functions {\n\t\tif fn.Pos() == pos {\n\t\t\treturn fn\n\t\t}\n\t}\n\treturn nil\n}\n\n// ValueForExpr returns the IR Value that corresponds to non-constant\n// expression e.\n//\n// It returns nil if no value was found, e.g.\n//   - the expression is not lexically contained within f;\n//   - f was not built with debug information; or\n//   - e is a constant expression.  (For efficiency, no debug\n//     information is stored for constants. Use\n//     go/types.Info.Types[e].Value instead.)\n//   - e is a reference to nil or a built-in function.\n//   - the value was optimised away.\n//\n// If e is an addressable expression used in an lvalue context,\n// value is the address denoted by e, and isAddr is true.\n//\n// The types of e (or &e, if isAddr) and the result are equal\n// (modulo \"untyped\" bools resulting from comparisons).\n//\n// (Tip: to find the ir.Value given a source position, use\n// astutil.PathEnclosingInterval to locate the ast.Node, then\n// EnclosingFunction to locate the Function, then ValueForExpr to find\n// the ir.Value.)\nfunc (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {\n\tif f.debugInfo() { // (opt)\n\t\te = unparen(e)\n\t\tfor _, b := range f.Blocks {\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\tif ref, ok := instr.(*DebugRef); ok {\n\t\t\t\t\tif ref.Expr == e {\n\t\t\t\t\t\treturn ref.X, ref.IsAddr\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// --- Lookup functions for source-level named entities (types.Objects) ---\n\n// Package returns the IR Package corresponding to the specified\n// type-checker package object.\n// It returns nil if no such IR package has been created.\nfunc (prog *Program) Package(obj *types.Package) *Package {\n\treturn prog.packages[obj]\n}\n\n// packageLevelValue returns the package-level value corresponding to\n// the specified named object, which may be a package-level const\n// (*Const), var (*Global) or func (*Function) of some package in\n// prog.  It returns nil if the object is not found.\nfunc (prog *Program) packageLevelValue(obj types.Object) Value {\n\tif pkg, ok := prog.packages[obj.Pkg()]; ok {\n\t\treturn pkg.values[obj]\n\t}\n\treturn nil\n}\n\n// FuncValue returns the concrete Function denoted by the source-level\n// named function obj, or nil if obj denotes an interface method.\n//\n// TODO(adonovan): check the invariant that obj.Type() matches the\n// result's Signature, both in the params/results and in the receiver.\nfunc (prog *Program) FuncValue(obj *types.Func) *Function {\n\tobj = obj.Origin()\n\tfn, _ := prog.packageLevelValue(obj).(*Function)\n\treturn fn\n}\n\n// ConstValue returns the IR Value denoted by the source-level named\n// constant obj.\nfunc (prog *Program) ConstValue(obj *types.Const) *Const {\n\t// TODO(adonovan): opt: share (don't reallocate)\n\t// Consts for const objects and constant ast.Exprs.\n\n\t// Universal constant? {true,false,nil}\n\tif obj.Parent() == types.Universe {\n\t\treturn NewConst(obj.Val(), obj.Type(), nil)\n\t}\n\t// Package-level named constant?\n\tif v := prog.packageLevelValue(obj); v != nil {\n\t\treturn v.(*Const)\n\t}\n\treturn NewConst(obj.Val(), obj.Type(), nil)\n}\n\n// VarValue returns the IR Value that corresponds to a specific\n// identifier denoting the source-level named variable obj.\n//\n// VarValue returns nil if a local variable was not found, perhaps\n// because its package was not built, the debug information was not\n// requested during IR construction, or the value was optimized away.\n//\n// ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),\n// and that ident must resolve to obj.\n//\n// pkg is the package enclosing the reference.  (A reference to a var\n// always occurs within a function, so we need to know where to find it.)\n//\n// If the identifier is a field selector and its base expression is\n// non-addressable, then VarValue returns the value of that field.\n// For example:\n//\n//\tfunc f() struct {x int}\n//\tf().x  // VarValue(x) returns a *Field instruction of type int\n//\n// All other identifiers denote addressable locations (variables).\n// For them, VarValue may return either the variable's address or its\n// value, even when the expression is evaluated only for its value; the\n// situation is reported by isAddr, the second component of the result.\n//\n// If !isAddr, the returned value is the one associated with the\n// specific identifier.  For example,\n//\n//\tvar x int    // VarValue(x) returns Const 0 here\n//\tx = 1        // VarValue(x) returns Const 1 here\n//\n// It is not specified whether the value or the address is returned in\n// any particular case, as it may depend upon optimizations performed\n// during IR code generation, such as registerization, constant\n// folding, avoidance of materialization of subexpressions, etc.\nfunc (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {\n\t// All references to a var are local to some function, possibly init.\n\tfn := EnclosingFunction(pkg, ref)\n\tif fn == nil {\n\t\treturn // e.g. def of struct field; IR not built?\n\t}\n\n\tid := ref[0].(*ast.Ident)\n\n\t// Defining ident of a parameter?\n\tif id.Pos() == obj.Pos() {\n\t\tfor _, param := range fn.Params {\n\t\t\tif param.Object() == obj {\n\t\t\t\treturn param, false\n\t\t\t}\n\t\t}\n\t}\n\n\t// Other ident?\n\tfor _, b := range fn.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tif dr, ok := instr.(*DebugRef); ok {\n\t\t\t\tif dr.Pos() == id.Pos() {\n\t\t\t\t\treturn dr.X, dr.IsAddr\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Defining ident of package-level var?\n\tif v := prog.packageLevelValue(obj); v != nil {\n\t\treturn v.(*Global), true\n\t}\n\n\treturn // e.g. debug info not requested, or var optimized away\n}\n"
  },
  {
    "path": "go/ir/source_test.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n//lint:file-ignore SA1019 go/ssa's test suite is built around the deprecated go/loader. We'll leave fixing that to upstream.\n\npackage ir_test\n\n// This file defines tests of source-level debugging utilities.\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\n\t\"golang.org/x/tools/go/analysis/analysistest\"\n\t\"golang.org/x/tools/go/expect\"\n\t\"golang.org/x/tools/go/loader\"\n)\n\nfunc TestObjValueLookup(t *testing.T) {\n\tif runtime.GOOS == \"android\" {\n\t\tt.Skipf(\"no testdata directory on %s\", runtime.GOOS)\n\t}\n\n\tconf := loader.Config{ParserMode: parser.ParseComments}\n\tsrc, err := ioutil.ReadFile(filepath.Join(analysistest.TestData(), \"objlookup.go\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\treadFile := func(filename string) ([]byte, error) { return src, nil }\n\tf, err := conf.ParseFile(filepath.Join(analysistest.TestData(), \"objlookup.go\"), src)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tconf.CreateFromFiles(\"main\", f)\n\n\t// Maps each var Ident (represented \"name:linenum\") to the\n\t// kind of ir.Value we expect (represented \"Constant\", \"&Alloc\").\n\texpectations := make(map[string]string)\n\n\t// Each note of the form @ir(x, \"BinOp\") in testdata/objlookup.go\n\t// specifies an expectation that an object named x declared on the\n\t// same line is associated with an ir.Value of type *ir.BinOp.\n\tnotes, err := expect.ExtractGo(conf.Fset, f)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfor _, n := range notes {\n\t\tif n.Name != \"ir\" {\n\t\t\tt.Errorf(\"%v: unexpected note type %q, want \\\"ir\\\"\", conf.Fset.Position(n.Pos), n.Name)\n\t\t\tcontinue\n\t\t}\n\t\tif len(n.Args) != 2 {\n\t\t\tt.Errorf(\"%v: ir has %d args, want 2\", conf.Fset.Position(n.Pos), len(n.Args))\n\t\t\tcontinue\n\t\t}\n\t\tident, ok := n.Args[0].(expect.Identifier)\n\t\tif !ok {\n\t\t\tt.Errorf(\"%v: got %v for arg 1, want identifier\", conf.Fset.Position(n.Pos), n.Args[0])\n\t\t\tcontinue\n\t\t}\n\t\texp, ok := n.Args[1].(string)\n\t\tif !ok {\n\t\t\tt.Errorf(\"%v: got %v for arg 2, want string\", conf.Fset.Position(n.Pos), n.Args[1])\n\t\t\tcontinue\n\t\t}\n\t\tp, _, err := expect.MatchBefore(conf.Fset, readFile, n.Pos, string(ident))\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\t\tpos := conf.Fset.Position(p)\n\t\tkey := fmt.Sprintf(\"%s:%d\", ident, pos.Line)\n\t\texpectations[key] = exp\n\t}\n\n\tiprog, err := conf.Load()\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\n\tprog := irutil.CreateProgram(iprog, 0 /*|ir.PrintFunctions*/)\n\tmainInfo := iprog.Created[0]\n\tmainPkg := prog.Package(mainInfo.Pkg)\n\tmainPkg.SetDebugMode(true)\n\tmainPkg.Build()\n\n\tvar varIds []*ast.Ident\n\tvar varObjs []*types.Var\n\tfor id, obj := range mainInfo.Defs {\n\t\t// Check invariants for func and const objects.\n\t\tswitch obj := obj.(type) {\n\t\tcase *types.Func:\n\t\t\tcheckFuncValue(t, prog, obj)\n\n\t\tcase *types.Const:\n\t\t\tcheckConstValue(t, prog, obj)\n\n\t\tcase *types.Var:\n\t\t\tif id.Name == \"_\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvarIds = append(varIds, id)\n\t\t\tvarObjs = append(varObjs, obj)\n\t\t}\n\t}\n\tfor id, obj := range mainInfo.Uses {\n\t\tif obj, ok := obj.(*types.Var); ok {\n\t\t\tvarIds = append(varIds, id)\n\t\t\tvarObjs = append(varObjs, obj)\n\t\t}\n\t}\n\n\t// Check invariants for var objects.\n\t// The result varies based on the specific Ident.\n\tfor i, id := range varIds {\n\t\tobj := varObjs[i]\n\t\tref, _ := astutil.PathEnclosingInterval(f, id.Pos(), id.Pos())\n\t\tpos := prog.Fset.Position(id.Pos())\n\t\texp := expectations[fmt.Sprintf(\"%s:%d\", id.Name, pos.Line)]\n\t\tif exp == \"\" {\n\t\t\tt.Errorf(\"%s: no expectation for var ident %s \", pos, id.Name)\n\t\t\tcontinue\n\t\t}\n\t\twantAddr := false\n\t\tif exp[0] == '&' {\n\t\t\twantAddr = true\n\t\t\texp = exp[1:]\n\t\t}\n\t\tcheckVarValue(t, prog, mainPkg, ref, obj, exp, wantAddr)\n\t}\n}\n\nfunc checkFuncValue(t *testing.T, prog *ir.Program, obj *types.Func) {\n\tfn := prog.FuncValue(obj)\n\t// fmt.Printf(\"FuncValue(%s) = %s\\n\", obj, fn) // debugging\n\tif fn == nil {\n\t\tif obj.Name() != \"interfaceMethod\" {\n\t\t\tt.Errorf(\"FuncValue(%s) == nil\", obj)\n\t\t}\n\t\treturn\n\t}\n\tif fnobj := fn.Object(); fnobj != obj {\n\t\tt.Errorf(\"FuncValue(%s).Object() == %s; value was %s\",\n\t\t\tobj, fnobj, fn.Name())\n\t\treturn\n\t}\n\tif !types.Identical(fn.Type(), obj.Type()) {\n\t\tt.Errorf(\"FuncValue(%s).Type() == %s\", obj, fn.Type())\n\t\treturn\n\t}\n}\n\nfunc checkConstValue(t *testing.T, prog *ir.Program, obj *types.Const) {\n\tc := prog.ConstValue(obj)\n\t// fmt.Printf(\"ConstValue(%s) = %s\\n\", obj, c) // debugging\n\tif c == nil {\n\t\tt.Errorf(\"ConstValue(%s) == nil\", obj)\n\t\treturn\n\t}\n\tif !types.Identical(c.Type(), obj.Type()) {\n\t\tt.Errorf(\"ConstValue(%s).Type() == %s\", obj, c.Type())\n\t\treturn\n\t}\n\tif obj.Name() != \"nil\" {\n\t\tif !constant.Compare(c.Value, token.EQL, obj.Val()) {\n\t\t\tt.Errorf(\"ConstValue(%s).Value (%s) != %s\",\n\t\t\t\tobj, c.Value, obj.Val())\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc checkVarValue(t *testing.T, prog *ir.Program, pkg *ir.Package, ref []ast.Node, obj *types.Var, expKind string, wantAddr bool) {\n\t// The prefix of all assertions messages.\n\tprefix := fmt.Sprintf(\"VarValue(%s @ L%d)\",\n\t\tobj, prog.Fset.Position(ref[0].Pos()).Line)\n\n\tv, gotAddr := prog.VarValue(obj, pkg, ref)\n\n\t// Kind is the concrete type of the ir Value.\n\tgotKind := \"nil\"\n\tif v != nil {\n\t\tgotKind = fmt.Sprintf(\"%T\", v)[len(\"*ir.\"):]\n\t}\n\n\t// fmt.Printf(\"%s = %v (kind %q; expect %q) wantAddr=%t gotAddr=%t\\n\", prefix, v, gotKind, expKind, wantAddr, gotAddr) // debugging\n\n\t// Check the kinds match.\n\t// \"nil\" indicates expected failure (e.g. optimized away).\n\tif expKind != gotKind {\n\t\tt.Errorf(\"%s concrete type == %s, want %s\", prefix, gotKind, expKind)\n\t}\n\n\t// Check the types match.\n\t// If wantAddr, the expected type is the object's address.\n\tif v != nil {\n\t\texpType := obj.Type()\n\t\tif wantAddr {\n\t\t\texpType = types.NewPointer(expType)\n\t\t\tif !gotAddr {\n\t\t\t\tt.Errorf(\"%s: got value, want address\", prefix)\n\t\t\t}\n\t\t} else if gotAddr {\n\t\t\tt.Errorf(\"%s: got address, want value\", prefix)\n\t\t}\n\t\tif !types.Identical(v.Type(), expType) {\n\t\t\tt.Errorf(\"%s.Type() == %s, want %s\", prefix, v.Type(), expType)\n\t\t}\n\t}\n}\n\n// Ensure that, in debug mode, we can determine the ir.Value\n// corresponding to every ast.Expr.\nfunc TestValueForExpr(t *testing.T) {\n\ttestValueForExpr(t, filepath.Join(analysistest.TestData(), \"valueforexpr.go\"))\n}\n\nfunc testValueForExpr(t *testing.T, testfile string) {\n\tif runtime.GOOS == \"android\" {\n\t\tt.Skipf(\"no testdata dir on %s\", runtime.GOOS)\n\t}\n\n\tconf := loader.Config{ParserMode: parser.ParseComments}\n\tf, err := conf.ParseFile(testfile, nil)\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\tconf.CreateFromFiles(\"main\", f)\n\n\tiprog, err := conf.Load()\n\tif err != nil {\n\t\tt.Error(err)\n\t\treturn\n\t}\n\n\tmainInfo := iprog.Created[0]\n\n\tprog := irutil.CreateProgram(iprog, 0)\n\tmainPkg := prog.Package(mainInfo.Pkg)\n\tmainPkg.SetDebugMode(true)\n\tmainPkg.Build()\n\n\tif false {\n\t\t// debugging\n\t\tfor _, mem := range mainPkg.Members {\n\t\t\tif fn, ok := mem.(*ir.Function); ok {\n\t\t\t\tfn.WriteTo(os.Stderr)\n\t\t\t}\n\t\t}\n\t}\n\n\tvar parenExprs []*ast.ParenExpr\n\tast.Inspect(f, func(n ast.Node) bool {\n\t\tif n != nil {\n\t\t\tif e, ok := n.(*ast.ParenExpr); ok {\n\t\t\t\tparenExprs = append(parenExprs, e)\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\n\tnotes, err := expect.ExtractGo(prog.Fset, f)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfor _, n := range notes {\n\t\twant := n.Name\n\t\tif want == \"nil\" {\n\t\t\twant = \"<nil>\"\n\t\t}\n\t\tposition := prog.Fset.Position(n.Pos)\n\t\tvar e ast.Expr\n\t\tfor _, paren := range parenExprs {\n\t\t\tif paren.Pos() > n.Pos {\n\t\t\t\te = paren.X\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif e == nil {\n\t\t\tt.Errorf(\"%s: note doesn't precede ParenExpr: %q\", position, want)\n\t\t\tcontinue\n\t\t}\n\n\t\tpath, _ := astutil.PathEnclosingInterval(f, n.Pos, n.Pos)\n\t\tif path == nil {\n\t\t\tt.Errorf(\"%s: can't find AST path from root to comment: %s\", position, want)\n\t\t\tcontinue\n\t\t}\n\n\t\tfn := ir.EnclosingFunction(mainPkg, path)\n\t\tif fn == nil {\n\t\t\tt.Errorf(\"%s: can't find enclosing function\", position)\n\t\t\tcontinue\n\t\t}\n\n\t\tv, gotAddr := fn.ValueForExpr(e) // (may be nil)\n\t\tgot := strings.TrimPrefix(fmt.Sprintf(\"%T\", v), \"*ir.\")\n\t\tif got != want {\n\t\t\tt.Errorf(\"%s: got value %q, want %q\", position, got, want)\n\t\t}\n\t\tif v != nil {\n\t\t\tT := v.Type()\n\t\t\tif gotAddr {\n\t\t\t\tT = T.Underlying().(*types.Pointer).Elem() // deref\n\t\t\t}\n\t\t\tif !types.Identical(T, mainInfo.TypeOf(e)) {\n\t\t\t\tt.Errorf(\"%s: got type %s, want %s\", position, mainInfo.TypeOf(e), T)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// findInterval parses input and returns the [start, end) positions of\n// the first occurrence of substr in input.  f==nil indicates failure;\n// an error has already been reported in that case.\nfunc findInterval(t *testing.T, fset *token.FileSet, input, substr string) (f *ast.File, start, end token.Pos) {\n\tf, err := parser.ParseFile(fset, \"<input>\", input, parser.SkipObjectResolution)\n\tif err != nil {\n\t\tt.Errorf(\"parse error: %s\", err)\n\t\treturn\n\t}\n\n\ti := strings.Index(input, substr)\n\tif i < 0 {\n\t\tt.Errorf(\"%q is not a substring of input\", substr)\n\t\tf = nil\n\t\treturn\n\t}\n\n\tfilePos := fset.File(f.Package)\n\treturn f, filePos.Pos(i), filePos.Pos(i + len(substr))\n}\n\nfunc TestEnclosingFunction(t *testing.T) {\n\ttests := []struct {\n\t\tinput  string // the input file\n\t\tsubstr string // first occurrence of this string denotes interval\n\t\tfn     string // name of expected containing function\n\t}{\n\t\t// We use distinctive numbers as syntactic landmarks.\n\n\t\t// Ordinary function:\n\t\t{`package main\n\t\t  func f() { println(1003) }`,\n\t\t\t\"100\", \"main.f\"},\n\t\t// Methods:\n\t\t{`package main\n                  type T int\n\t\t  func (t T) f() { println(200) }`,\n\t\t\t\"200\", \"(main.T).f\"},\n\t\t// Function literal:\n\t\t{`package main\n\t\t  func f() { println(func() { print(300) }) }`,\n\t\t\t\"300\", \"main.f$1\"},\n\t\t// Doubly nested\n\t\t{`package main\n\t\t  func f() { println(func() { print(func() { print(350) })})}`,\n\t\t\t\"350\", \"main.f$1$1\"},\n\t\t// Implicit init for package-level var initializer.\n\t\t{\"package main; var a = 400\", \"400\", \"main.init\"},\n\t\t// No code for constants:\n\t\t{\"package main; const a = 500\", \"500\", \"(none)\"},\n\t\t// Explicit init()\n\t\t{\"package main; func init() { println(600) }\", \"600\", \"main.init#1\"},\n\t\t// Multiple explicit init functions:\n\t\t{`package main\n\t\t  func init() { println(\"foo\") }\n\t\t  func init() { println(800) }`,\n\t\t\t\"800\", \"main.init#2\"},\n\t\t// init() containing FuncLit.\n\t\t{`package main\n\t\t  func init() { println(func(){print(900)}) }`,\n\t\t\t\"900\", \"main.init#1$1\"},\n\t}\n\tfor _, test := range tests {\n\t\tconf := loader.Config{Fset: token.NewFileSet()}\n\t\tf, start, end := findInterval(t, conf.Fset, test.input, test.substr)\n\t\tif f == nil {\n\t\t\tcontinue\n\t\t}\n\t\tpath, exact := astutil.PathEnclosingInterval(f, start, end)\n\t\tif !exact {\n\t\t\tt.Errorf(\"EnclosingFunction(%q) not exact\", test.substr)\n\t\t\tcontinue\n\t\t}\n\n\t\tconf.CreateFromFiles(\"main\", f)\n\n\t\tiprog, err := conf.Load()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\t\tprog := irutil.CreateProgram(iprog, 0)\n\t\tpkg := prog.Package(iprog.Created[0].Pkg)\n\t\tpkg.Build()\n\n\t\tname := \"(none)\"\n\t\tfn := ir.EnclosingFunction(pkg, path)\n\t\tif fn != nil {\n\t\t\tname = fn.String()\n\t\t}\n\n\t\tif name != test.fn {\n\t\t\tt.Errorf(\"EnclosingFunction(%q in %q) got %s, want %s\",\n\t\t\t\ttest.substr, test.input, name, test.fn)\n\t\t\tcontinue\n\t\t}\n\n\t\t// While we're here: test HasEnclosingFunction.\n\t\tif has := ir.HasEnclosingFunction(pkg, path); has != (fn != nil) {\n\t\t\tt.Errorf(\"HasEnclosingFunction(%q in %q) got %v, want %v\",\n\t\t\t\ttest.substr, test.input, has, fn != nil)\n\t\t\tcontinue\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "go/ir/ssa.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This package defines a high-level intermediate representation for\n// Go programs using static single-information (SSI) form.\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"math/big\"\n\t\"sync\"\n\n\t\"honnef.co/go/tools/go/types/typeutil\"\n)\n\nconst (\n\t// Replace CompositeValue with only constant values with AggregateConst. Currently disabled because it breaks field\n\t// tracking in U1000.\n\tdoSimplifyConstantCompositeValues = false\n)\n\ntype ID int\n\n// A Program is a partial or complete Go program converted to IR form.\ntype Program struct {\n\tFset       *token.FileSet              // position information for the files of this Program\n\tPrintFunc  string                      // create ir.html for function specified in PrintFunc\n\timported   map[string]*Package         // all importable Packages, keyed by import path\n\tpackages   map[*types.Package]*Package // all loaded Packages, keyed by object\n\tmode       BuilderMode                 // set of mode bits for IR construction\n\tMethodSets typeutil.MethodSetCache     // cache of type-checker's method-sets\n\n\tmethodsMu    sync.Mutex               // guards the following maps:\n\tmethodSets   typeutil.Map[*methodSet] // maps type to its concrete methodSet\n\truntimeTypes typeutil.Map[bool]       // types for which rtypes are needed\n\tcanon        typeutil.Map[types.Type] // type canonicalization map\n\n\tnoReturn func(*types.Func) bool\n}\n\n// A Package is a single analyzed Go package containing Members for\n// all package-level functions, variables, constants and types it\n// declares.  These may be accessed directly via Members, or via the\n// type-specific accessor methods Func, Type, Var and Const.\n//\n// Members also contains entries for \"init\" (the synthetic package\n// initializer) and \"init#%d\", the nth declared init function,\n// and unspecified other things too.\ntype Package struct {\n\tProg      *Program               // the owning program\n\tPkg       *types.Package         // the corresponding go/types.Package\n\tMembers   map[string]Member      // all package members keyed by name (incl. init and init#%d)\n\tFunctions []*Function            // all functions, excluding anonymous ones\n\tvalues    map[types.Object]Value // package members (incl. types and methods), keyed by object\n\tinit      *Function              // Func(\"init\"); the package's init function\n\tdebug     bool                   // include full debug info in this package\n\tprintFunc string                 // which function to print in HTML form\n\n\t// The following fields are set transiently, then cleared\n\t// after building.\n\tbuildOnce   sync.Once           // ensures package building occurs once\n\tninit       int32               // number of init functions\n\tinfo        *types.Info         // package type information\n\tfiles       []*ast.File         // package ASTs\n\tinitVersion map[ast.Expr]string // goversion to use for each global var init expr\n}\n\n// A Member is a member of a Go package, implemented by *NamedConst,\n// *Global, *Function, or *Type; they are created by package-level\n// const, var, func and type declarations respectively.\ntype Member interface {\n\tName() string                    // declared name of the package member\n\tString() string                  // package-qualified name of the package member\n\tRelString(*types.Package) string // like String, but relative refs are unqualified\n\tObject() types.Object            // typechecker's object for this member, if any\n\tType() types.Type                // type of the package member\n\tToken() token.Token              // token.{VAR,FUNC,CONST,TYPE}\n\tPackage() *Package               // the containing package\n}\n\n// A Type is a Member of a Package representing a package-level named type.\ntype Type struct {\n\tobject *types.TypeName\n\tpkg    *Package\n}\n\n// A NamedConst is a Member of a Package representing a package-level\n// named constant.\n//\n// Pos() returns the position of the declaring ast.ValueSpec.Names[*]\n// identifier.\n//\n// NB: a NamedConst is not a Value; it contains a constant Value, which\n// it augments with the name and position of its 'const' declaration.\ntype NamedConst struct {\n\tobject *types.Const\n\tValue  *Const\n\tpkg    *Package\n}\n\n// A Value is an IR value that can be referenced by an instruction.\ntype Value interface {\n\tsetID(ID)\n\n\t// Name returns the name of this value, and determines how\n\t// this Value appears when used as an operand of an\n\t// Instruction.\n\t//\n\t// This is the same as the source name for Parameters,\n\t// Builtins, Functions, FreeVars, Globals.\n\t// For constants, it is a representation of the constant's value\n\t// and type.  For all other Values this is the name of the\n\t// virtual register defined by the instruction.\n\t//\n\t// The name of an IR Value is not semantically significant,\n\t// and may not even be unique within a function.\n\tName() string\n\n\t// ID returns the ID of this value. IDs are unique within a single\n\t// function and are densely numbered, but may contain gaps.\n\t// Values and other Instructions share the same ID space.\n\t// Globally, values are identified by their addresses. However,\n\t// IDs exist to facilitate efficient storage of mappings between\n\t// values and data when analysing functions.\n\t//\n\t// NB: IDs are allocated late in the IR construction process and\n\t// are not available to early stages of said process.\n\tID() ID\n\n\t// If this value is an Instruction, String returns its\n\t// disassembled form; otherwise it returns unspecified\n\t// human-readable information about the Value, such as its\n\t// kind, name and type.\n\tString() string\n\n\t// Type returns the type of this value.  Many instructions\n\t// (e.g. IndexAddr) change their behaviour depending on the\n\t// types of their operands.\n\tType() types.Type\n\n\t// Parent returns the function to which this Value belongs.\n\t// It returns nil for named Functions, Builtin and Global.\n\tParent() *Function\n\n\t// Referrers returns the list of instructions that have this\n\t// value as one of their operands; it may contain duplicates\n\t// if an instruction has a repeated operand.\n\t//\n\t// Referrers actually returns a pointer through which the\n\t// caller may perform mutations to the object's state.\n\t//\n\t// Referrers is currently only defined if Parent()!=nil,\n\t// i.e. for the function-local values FreeVar, Parameter,\n\t// Functions (iff anonymous) and all value-defining instructions.\n\t// It returns nil for named Functions, Builtin and Global.\n\t//\n\t// Instruction.Operands contains the inverse of this relation.\n\tReferrers() *[]Instruction\n\n\tOperands(rands []*Value) []*Value // nil for non-Instructions\n\n\t// Source returns the AST node responsible for creating this\n\t// value. A single AST node may be responsible for more than one\n\t// value, and not all values have an associated AST node.\n\t//\n\t// Do not use this method to find a Value given an ast.Expr; use\n\t// ValueForExpr instead.\n\tSource() ast.Node\n\n\t// Pos returns Source().Pos() if Source is not nil, else it\n\t// returns token.NoPos.\n\tPos() token.Pos\n}\n\n// An Instruction is an IR instruction that computes a new Value or\n// has some effect.\n//\n// An Instruction that defines a value (e.g. BinOp) also implements\n// the Value interface; an Instruction that only has an effect (e.g. Store)\n// does not.\ntype Instruction interface {\n\tsetSource(ast.Node)\n\tsetID(ID)\n\n\tComment() string\n\n\t// String returns the disassembled form of this value.\n\t//\n\t// Examples of Instructions that are Values:\n\t//       \"BinOp <int> {+} t1 t2\"  (BinOp)\n\t//       \"Call <int> len t1\"      (Call)\n\t// Note that the name of the Value is not printed.\n\t//\n\t// Examples of Instructions that are not Values:\n\t//       \"Return t1\"              (Return)\n\t//       \"Store {int} t2 t1\"      (Store)\n\t//\n\t// (The separation of Value.Name() from Value.String() is useful\n\t// for some analyses which distinguish the operation from the\n\t// value it defines, e.g., 'y = local int' is both an allocation\n\t// of memory 'local int' and a definition of a pointer y.)\n\tString() string\n\n\t// ID returns the ID of this instruction. IDs are unique within a single\n\t// function and are densely numbered, but may contain gaps.\n\t// Globally, instructions are identified by their addresses. However,\n\t// IDs exist to facilitate efficient storage of mappings between\n\t// instructions and data when analysing functions.\n\t//\n\t// NB: IDs are allocated late in the IR construction process and\n\t// are not available to early stages of said process.\n\tID() ID\n\n\t// Parent returns the function to which this instruction\n\t// belongs.\n\tParent() *Function\n\n\t// Block returns the basic block to which this instruction\n\t// belongs.\n\tBlock() *BasicBlock\n\n\t// setBlock sets the basic block to which this instruction belongs.\n\tsetBlock(*BasicBlock)\n\n\t// Operands returns the operands of this instruction: the\n\t// set of Values it references.\n\t//\n\t// Specifically, it appends their addresses to rands, a\n\t// user-provided slice, and returns the resulting slice,\n\t// permitting avoidance of memory allocation.\n\t//\n\t// The operands are appended in undefined order, but the order\n\t// is consistent for a given Instruction; the addresses are\n\t// always non-nil but may point to a nil Value.  Clients may\n\t// store through the pointers, e.g. to effect a value\n\t// renaming.\n\t//\n\t// Value.Referrers is a subset of the inverse of this\n\t// relation.  (Referrers are not tracked for all types of\n\t// Values.)\n\tOperands(rands []*Value) []*Value\n\n\tReferrers() *[]Instruction // nil for non-Values\n\n\t// Source returns the AST node responsible for creating this\n\t// instruction. A single AST node may be responsible for more than\n\t// one instruction, and not all instructions have an associated\n\t// AST node.\n\tSource() ast.Node\n\n\t// Pos returns Source().Pos() if Source is not nil, else it\n\t// returns token.NoPos.\n\tPos() token.Pos\n}\n\n// A Node is a node in the IR value graph.  Every concrete type that\n// implements Node is also either a Value, an Instruction, or both.\n//\n// Node contains the methods common to Value and Instruction, plus the\n// Operands and Referrers methods generalized to return nil for\n// non-Instructions and non-Values, respectively.\n//\n// Node is provided to simplify IR graph algorithms.  Clients should\n// use the more specific and informative Value or Instruction\n// interfaces where appropriate.\ntype Node interface {\n\tsetID(ID)\n\n\t// Common methods:\n\tID() ID\n\tString() string\n\tSource() ast.Node\n\tPos() token.Pos\n\tParent() *Function\n\n\t// Partial methods:\n\tOperands(rands []*Value) []*Value // nil for non-Instructions\n\tReferrers() *[]Instruction        // nil for non-Values\n}\n\ntype Synthetic int\n\nconst (\n\tSyntheticLoadedFromExportData Synthetic = iota + 1\n\tSyntheticPackageInitializer\n\tSyntheticThunk\n\tSyntheticWrapper\n\tSyntheticBound\n\tSyntheticGeneric\n\tSyntheticRangeOverFuncYield\n)\n\nfunc (syn Synthetic) String() string {\n\tswitch syn {\n\tcase SyntheticLoadedFromExportData:\n\t\treturn \"loaded from export data\"\n\tcase SyntheticPackageInitializer:\n\t\treturn \"package initializer\"\n\tcase SyntheticThunk:\n\t\treturn \"thunk\"\n\tcase SyntheticWrapper:\n\t\treturn \"wrapper\"\n\tcase SyntheticBound:\n\t\treturn \"bound\"\n\tcase SyntheticGeneric:\n\t\treturn \"generic\"\n\tcase SyntheticRangeOverFuncYield:\n\t\treturn \"range-over-func yield\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"Synthetic(%d)\", syn)\n\t}\n}\n\n// Function represents the parameters, results, and code of a function\n// or method.\n//\n// If Blocks is nil, this indicates an external function for which no\n// Go source code is available.  In this case, FreeVars, Locals, and\n// Params are nil too.  Clients performing whole-program analysis must\n// handle external functions specially.\n//\n// Blocks contains the function's control-flow graph (CFG).\n// Blocks[0] is the function entry point; block order is not otherwise\n// semantically significant, though it may affect the readability of\n// the disassembly.\n// To iterate over the blocks in dominance order, use DomPreorder().\n//\n// A nested function (Parent()!=nil) that refers to one or more\n// lexically enclosing local variables (\"free variables\") has FreeVars.\n// Such functions cannot be called directly but require a\n// value created by MakeClosure which, via its Bindings, supplies\n// values for these parameters.\n//\n// If the function is a method (Signature.Recv() != nil) then the first\n// element of Params is the receiver parameter.\n//\n// A Go package may declare many functions called \"init\".\n// For each one, Object().Name() returns \"init\" but Name() returns\n// \"init#1\", etc, in declaration order.\n//\n// Pos() returns the declaring ast.FuncLit.Type.Func or the position\n// of the ast.FuncDecl.Name, if the function was explicit in the\n// source.  Synthetic wrappers, for which Synthetic != \"\", may share\n// the same position as the function they wrap.\n// Syntax.Pos() always returns the position of the declaring \"func\" token.\n//\n// When the operand of a range statement is an iterator function,\n// the loop body is transformed into a synthetic anonymous function\n// that is passed as the yield argument in a call to the iterator.\n// In that case, Function.Source() is the ast.RangeStmt.\n//\n// Synthetic functions, for which Synthetic != \"\", are functions\n// that do not appear in the source AST. These include:\n//   - method wrappers,\n//   - thunks,\n//   - bound functions,\n//   - empty functions built from loaded type information,\n//   - yield functions created from range-over-func loops, and\n//   - package init functions.\n//\n// Type() returns the function's Signature.\ntype Function struct {\n\tnode\n\n\tname      string\n\tobject    *types.Func      // symbol for declared function (nil for FuncLit or synthetic init)\n\tmethod    *types.Selection // info about provenance of synthetic methods\n\tSignature *types.Signature\n\tgenerics  instanceWrapperMap\n\n\tSynthetic Synthetic // provenance of synthetic function; 0 for true source functions\n\tparent    *Function // enclosing function if anon; nil if global\n\tPkg       *Package  // enclosing package; nil for shared funcs (wrappers and error.Error)\n\tProg      *Program  // enclosing program\n\n\t// These fields are populated only when the function body is built:\n\n\tParams    []*Parameter  // function parameters; for methods, includes receiver\n\tFreeVars  []*FreeVar    // free variables whose values must be supplied by closure\n\tLocals    []*Alloc      // frame-allocated variables of this function\n\tBlocks    []*BasicBlock // basic blocks of the function; nil => external\n\tExit      *BasicBlock   // The function's exit block\n\tAnonFuncs []*Function   // anonymous functions (from FuncLit, RangeStmt) directly beneath this one\n\treferrers []Instruction // referring instructions (iff Parent() != nil)\n\n\tgoversion string // Go version of syntax (NB: init is special)\n\n\t// uniq is not stored in functionBody because we need it after function building finishes\n\tuniq int64 // source of unique ints within the source tree while building\n\n\t*functionBody\n}\n\ntype instanceWrapperMap struct {\n\th       typeutil.Hasher\n\tentries map[uint32][]struct {\n\t\tkey *types.TypeList\n\t\tval *Function\n\t}\n\tlen int\n}\n\nfunc typeListIdentical(l1, l2 *types.TypeList) bool {\n\tif l1.Len() != l2.Len() {\n\t\treturn false\n\t}\n\tfor i := 0; i < l1.Len(); i++ {\n\t\tt1 := l1.At(i)\n\t\tt2 := l2.At(i)\n\t\tif !types.Identical(t1, t2) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (m *instanceWrapperMap) At(key *types.TypeList) *Function {\n\tif m.entries == nil {\n\t\tm.entries = make(map[uint32][]struct {\n\t\t\tkey *types.TypeList\n\t\t\tval *Function\n\t\t})\n\t\tm.h = typeutil.MakeHasher()\n\t}\n\n\tvar hash uint32\n\tfor t := range key.Types() {\n\t\thash += m.h.Hash(t)\n\t}\n\n\tfor _, e := range m.entries[hash] {\n\t\tif typeListIdentical(e.key, key) {\n\t\t\treturn e.val\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (m *instanceWrapperMap) Set(key *types.TypeList, val *Function) {\n\tif m.entries == nil {\n\t\tm.entries = make(map[uint32][]struct {\n\t\t\tkey *types.TypeList\n\t\t\tval *Function\n\t\t})\n\t\tm.h = typeutil.MakeHasher()\n\t}\n\n\tvar hash uint32\n\tfor t := range key.Types() {\n\t\thash += m.h.Hash(t)\n\t}\n\tfor i, e := range m.entries[hash] {\n\t\tif typeListIdentical(e.key, key) {\n\t\t\tm.entries[hash][i].val = val\n\t\t\treturn\n\t\t}\n\t}\n\tm.entries[hash] = append(m.entries[hash], struct {\n\t\tkey *types.TypeList\n\t\tval *Function\n\t}{key, val})\n\tm.len++\n}\n\nfunc (m *instanceWrapperMap) Len() int {\n\treturn m.len\n}\n\ntype constValue struct {\n\tc   Constant\n\tidx int\n}\n\ntype functionBody struct {\n\t// The following fields are set transiently during building,\n\t// then cleared.\n\tcurrentBlock *BasicBlock              // where to emit code\n\tvars         map[*types.Var]Value     // addresses of local variables\n\tresults      []*Alloc                 // result allocations of the current function\n\treturnVars   []*types.Var             // variables for a return statement. Either results or for range-over-func a parent's results\n\ttargets      *targets                 // linked stack of branch targets\n\tlblocks      map[*types.Label]*lblock // labelled blocks\n\tjump         *types.Var               // synthetic variable for the yield state (non-nil => range-over-func)\n\tdeferstack   *types.Var               // synthetic variable holding enclosing ssa:deferstack()\n\tsourceFn     *Function                // nearest enclosing source function\n\texits        []*exit                  // exits of the function that need to be resolved\n\n\tconsts          map[constKey]constValue\n\taggregateConsts typeutil.Map[[]*AggregateConst]\n\n\twr        *HTMLWriter\n\tfakeExits BlockSet\n\tblocksets [5]BlockSet\n\thasDefer  bool\n\n\t// a contiguous block of instructions that will be used by blocks,\n\t// to avoid making multiple allocations.\n\tscratchInstructions []Instruction\n}\n\n// BasicBlock represents an IR basic block.\n//\n// The final element of Instrs is always an explicit transfer of\n// control (If, Jump, Return, Panic, or Unreachable).\n//\n// A block may contain no Instructions only if it is unreachable,\n// i.e., Preds is nil.  Empty blocks are typically pruned.\n//\n// BasicBlocks and their Preds/Succs relation form a (possibly cyclic)\n// graph independent of the IR Value graph: the control-flow graph or\n// CFG.  It is illegal for multiple edges to exist between the same\n// pair of blocks.\n//\n// Each BasicBlock is also a node in the dominator tree of the CFG.\n// The tree may be navigated using Idom()/Dominees() and queried using\n// Dominates().\n//\n// The order of Preds and Succs is significant (to Phi and If\n// instructions, respectively).\ntype BasicBlock struct {\n\tIndex        int            // index of this block within Parent().Blocks\n\tComment      string         // optional label; no semantic significance\n\tparent       *Function      // parent function\n\tInstrs       []Instruction  // instructions in order\n\tPreds, Succs []*BasicBlock  // predecessors and successors\n\tsuccs2       [2]*BasicBlock // initial space for Succs\n\tdom          domInfo        // dominator tree info\n\tpdom         domInfo        // post-dominator tree info\n\tpost         int\n\tgaps         int // number of nil Instrs (transient)\n\trundefers    int // number of rundefers (transient)\n}\n\n// Pure values ----------------------------------------\n\n// A FreeVar represents a free variable of the function to which it\n// belongs.\n//\n// FreeVars are used to implement anonymous functions, whose free\n// variables are lexically captured in a closure formed by\n// MakeClosure.  The value of such a free var is an Alloc or another\n// FreeVar and is considered a potentially escaping heap address, with\n// pointer type.\n//\n// FreeVars are also used to implement bound method closures.  Such a\n// free var represents the receiver value and may be of any type that\n// has concrete methods.\n//\n// Pos() returns the position of the value that was captured, which\n// belongs to an enclosing function.\ntype FreeVar struct {\n\tnode\n\n\tname      string\n\ttyp       types.Type\n\tparent    *Function\n\treferrers []Instruction\n\n\t// Transiently needed during building.\n\touter Value // the Value captured from the enclosing context.\n}\n\n// A Parameter represents an input parameter of a function.\ntype Parameter struct {\n\tregister\n\n\tname   string\n\tobject *types.Var // non-nil\n}\n\n// A Const represents the value of a constant expression.\n//\n// The underlying type of a constant may be any boolean, numeric, or\n// string type.  In addition, a Const may represent the nil value of\n// any reference type---interface, map, channel, pointer, slice, or\n// function---but not \"untyped nil\".\n//\n// All source-level constant expressions are represented by a Const\n// of the same type and value.\n//\n// Value holds the exact value of the constant, independent of its\n// Type(), using the same representation as package go/constant uses for\n// constants, or nil for a typed nil value.\n//\n// Pos() returns token.NoPos.\n//\n// Example printed form:\n//\n//\tConst <int> {42}\n//\tConst <untyped string> {\"test\"}\n//\tConst <MyComplex> {(3 + 4i)}\ntype Const struct {\n\tregister\n\n\tValue constant.Value\n}\n\ntype AggregateConst struct {\n\tregister\n\n\tValues []Value\n}\n\ntype CompositeValue struct {\n\tregister\n\n\t// Bitmap records which elements were explicitly provided. For example, [4]byte{2: x} would have a bitmap of 0010.\n\tBitmap big.Int\n\t// The number of bits set in Bitmap\n\tNumSet int\n\t// Dense list of values in the composite literal. Omitted elements are filled in with zero values.\n\tValues []Value\n}\n\n// TODO add the element's zero constant to ArrayConst\ntype ArrayConst struct {\n\tregister\n}\n\ntype GenericConst struct {\n\tregister\n}\n\ntype Constant interface {\n\tInstruction\n\tValue\n\taConstant()\n\tRelString(*types.Package) string\n\tequal(Constant) bool\n\tsetType(types.Type)\n}\n\nfunc (*Const) aConstant()          {}\nfunc (*AggregateConst) aConstant() {}\nfunc (*ArrayConst) aConstant()     {}\nfunc (*GenericConst) aConstant()   {}\n\n// A Global is a named Value holding the address of a package-level\n// variable.\n//\n// Pos() returns the position of the ast.ValueSpec.Names[*]\n// identifier.\ntype Global struct {\n\tnode\n\n\tname   string\n\tobject types.Object // a *types.Var; may be nil for synthetics e.g. init$guard\n\ttyp    types.Type\n\n\tPkg *Package\n}\n\n// A Builtin represents a specific use of a built-in function, e.g. len.\n//\n// Builtins are immutable values.  Builtins do not have addresses.\n// Builtins can only appear in CallCommon.Func.\n//\n// Name() indicates the function: one of the built-in functions from the\n// Go spec (excluding \"make\" and \"new\") or one of these ir-defined\n// intrinsics:\n//\n//\t// wrapnilchk returns ptr if non-nil, panics otherwise.\n//\t// (For use in indirection wrappers.)\n//\tfunc ir:wrapnilchk(ptr *T, recvType, methodName string) *T\n//\n//\t// noreturnWasPanic returns true if the previously called\n//\t// function panicked, false if it exited the process.\n//\tfunc ir:noreturnWasPanic() bool\n//\n// Object() returns a *types.Builtin for built-ins defined by the spec,\n// nil for others.\n//\n// Type() returns a *types.Signature representing the effective\n// signature of the built-in for this call.\ntype Builtin struct {\n\tnode\n\n\tname string\n\tsig  *types.Signature\n}\n\n// Value-defining instructions  ----------------------------------------\n\n// The Alloc instruction reserves space for a variable of the given type,\n// zero-initializes it, and yields its address.\n//\n// Alloc values are always addresses, and have pointer types, so the\n// type of the allocated variable is actually\n// Type().Underlying().(*types.Pointer).Elem().\n//\n// If Heap is false, Alloc zero-initializes the same local variable in\n// the call frame and returns its address; in this case the Alloc must\n// be present in Function.Locals. We call this a \"local\" alloc.\n//\n// If Heap is true, Alloc allocates a new zero-initialized variable\n// each time the instruction is executed. We call this a \"new\" alloc.\n//\n// When Alloc is applied to a channel, map or slice type, it returns\n// the address of an uninitialized (nil) reference of that kind; store\n// the result of MakeSlice, MakeMap or MakeChan in that location to\n// instantiate these types.\n//\n// Pos() returns the ast.CompositeLit.Lbrace for a composite literal,\n// or the ast.CallExpr.Rparen for a call to new() or for a call that\n// allocates a varargs slice.\n//\n// Example printed form:\n//\n//\tt1 = StackAlloc <*int>\n//\tt2 = HeapAlloc <*int> (new)\ntype Alloc struct {\n\tregister\n\tHeap  bool\n\tindex int // dense numbering; for lifting\n}\n\nvar _ Instruction = (*Sigma)(nil)\nvar _ Value = (*Sigma)(nil)\n\n// The Sigma instruction represents an SSI σ-node, which splits values\n// at branches in the control flow.\n//\n// Conceptually, σ-nodes exist at the end of blocks that branch and\n// constitute parallel assignments to one value per destination block.\n// However, such a representation would be awkward to work with, so\n// instead we place σ-nodes at the beginning of branch targets. The\n// From field denotes to which incoming edge the node applies.\n//\n// Within a block, all σ-nodes must appear before all non-σ nodes.\n//\n// Example printed form:\n//\n//\tt2 = Sigma <int> [#0] t1 (x)\ntype Sigma struct {\n\tregister\n\tFrom *BasicBlock\n\tX    Value\n\n\tlive bool // used during lifting\n}\n\ntype CopyInfo uint64\n\nconst (\n\tCopyInfoUnspecified CopyInfo = 0\n\tCopyInfoNotNil      CopyInfo = 1 << iota\n\tCopyInfoNotZeroLength\n\tCopyInfoNotNegative\n\tCopyInfoSingleConcreteType\n\tCopyInfoClosed\n)\n\ntype Copy struct {\n\tregister\n\tX    Value\n\tWhy  Instruction\n\tInfo CopyInfo\n}\n\n// The Phi instruction represents an SSA φ-node, which combines values\n// that differ across incoming control-flow edges and yields a new\n// value.  Within a block, all φ-nodes must appear before all non-φ, non-σ\n// nodes.\n//\n// Pos() returns the position of the && or || for short-circuit\n// control-flow joins, or that of the *Alloc for φ-nodes inserted\n// during SSA renaming.\n//\n// Example printed form:\n//\n//\tt3 = Phi <int> 2:t1 4:t2 (x)\ntype Phi struct {\n\tregister\n\tEdges []Value // Edges[i] is value for Block().Preds[i]\n\n\tlive bool // used during lifting\n}\n\n// The Call instruction represents a function or method call.\n//\n// The Call instruction yields the function result if there is exactly\n// one.  Otherwise it returns a tuple, the components of which are\n// accessed via Extract.\n//\n// See CallCommon for generic function call documentation.\n//\n// Pos() returns the ast.CallExpr.Lparen, if explicit in the source.\n//\n// Example printed form:\n//\n//\tt3 = Call <()> println t1 t2\n//\tt4 = Call <()> foo$1\n//\tt6 = Invoke <string> t5.String\ntype Call struct {\n\tregister\n\tCall CallCommon\n}\n\n// The BinOp instruction yields the result of binary operation X Op Y.\n//\n// Pos() returns the ast.BinaryExpr.OpPos, if explicit in the source.\n//\n// Example printed form:\n//\n//\tt3 = BinOp <int> {+} t2 t1\ntype BinOp struct {\n\tregister\n\t// One of:\n\t// ADD SUB MUL QUO REM          + - * / %\n\t// AND OR XOR SHL SHR AND_NOT   & | ^ << >> &^\n\t// EQL NEQ LSS LEQ GTR GEQ      == != < <= < >=\n\tOp   token.Token\n\tX, Y Value\n}\n\n// The UnOp instruction yields the result of Op X.\n// XOR is bitwise complement.\n// SUB is negation.\n// NOT is logical negation.\n//\n// Example printed form:\n//\n//\tt2 = UnOp <int> {^} t1\ntype UnOp struct {\n\tregister\n\tOp token.Token // One of: NOT SUB XOR ! - ^\n\tX  Value\n}\n\n// The Load instruction loads a value from a memory address.\n//\n// For implicit memory loads, Pos() returns the position of the\n// most closely associated source-level construct; the details are not\n// specified.\n//\n// Example printed form:\n//\n//\tt2 = Load <int> t1\ntype Load struct {\n\tregister\n\tX Value\n}\n\n// The ChangeType instruction applies to X a value-preserving type\n// change to Type().\n//\n// Type changes are permitted:\n//   - between a named type and its underlying type.\n//   - between two named types of the same underlying type.\n//   - between (possibly named) pointers to identical base types.\n//   - from a bidirectional channel to a read- or write-channel,\n//     optionally adding/removing a name.\n//\n// This operation cannot fail dynamically.\n//\n// Pos() returns the ast.CallExpr.Lparen, if the instruction arose\n// from an explicit conversion in the source.\n//\n// Example printed form:\n//\n//\tt2 = ChangeType <*T> t1\ntype ChangeType struct {\n\tregister\n\tX Value\n}\n\n// The Convert instruction yields the conversion of value X to type\n// Type().  One or both of those types is basic (but possibly named).\n//\n// A conversion may change the value and representation of its operand.\n// Conversions are permitted:\n//   - between real numeric types.\n//   - between complex numeric types.\n//   - between string and []byte or []rune.\n//   - between pointers and unsafe.Pointer.\n//   - between unsafe.Pointer and uintptr.\n//   - from (Unicode) integer to (UTF-8) string.\n//\n// A conversion may imply a type name change also.\n//\n// This operation cannot fail dynamically.\n//\n// Conversions of untyped string/number/bool constants to a specific\n// representation are eliminated during IR construction.\n//\n// Pos() returns the ast.CallExpr.Lparen, if the instruction arose\n// from an explicit conversion in the source.\n//\n// Example printed form:\n//\n//\tt2 = Convert <[]byte> t1\ntype Convert struct {\n\tregister\n\tX Value\n}\n\n// The MultiConvert instruction yields the conversion of value X to type\n// Type(). Either X.Type() or Type() must be a type parameter. Each\n// type in the type set of X.Type() can be converted to each type in the\n// type set of Type().\n//\n// See the documentation for Convert, ChangeType, SliceToArray, and SliceToArrayPointer\n// for the conversions that are permitted.\n//\n// This operation can fail dynamically (see SliceToArrayPointer).\n//\n// Example printed form:\n//\n//\tt1 = multiconvert D <- S (t0) [*[2]rune <- []rune | string <- []rune]\ntype MultiConvert struct {\n\tregister\n\tX    Value\n\tfrom typeutil.TypeSet\n\tto   typeutil.TypeSet\n}\n\n// ChangeInterface constructs a value of one interface type from a\n// value of another interface type known to be assignable to it.\n// This operation cannot fail.\n//\n// Pos() returns the ast.CallExpr.Lparen if the instruction arose from\n// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the\n// instruction arose from an explicit e.(T) operation; or token.NoPos\n// otherwise.\n//\n// Example printed form:\n//\n//\tt2 = ChangeInterface <I1> t1\ntype ChangeInterface struct {\n\tregister\n\tX Value\n}\n\n// The SliceToArrayPointer instruction yields the conversion of slice X to\n// array pointer.\n//\n// Pos() returns the ast.CallExpr.Lparen, if the instruction arose\n// from an explicit conversion in the source.\n//\n// Example printed form:\n//\n//\tt2 = SliceToArrayPointer <*[4]byte> t1\ntype SliceToArrayPointer struct {\n\tregister\n\tX Value\n}\n\n// The SliceToArray instruction yields the conversion of slice X to\n// array.\n//\n// Pos() returns the ast.CallExpr.Lparen, if the instruction arose\n// from an explicit conversion in the source.\n//\n// Example printed form:\n//\n//\tt2 = SliceToArray <[4]byte> t1\ntype SliceToArray struct {\n\tregister\n\tX Value\n}\n\n// MakeInterface constructs an instance of an interface type from a\n// value of a concrete type.\n//\n// Use Program.MethodSets.MethodSet(X.Type()) to find the method-set\n// of X, and Program.MethodValue(m) to find the implementation of a method.\n//\n// To construct the zero value of an interface type T, use:\n//\n//\tNewConst(constant.MakeNil(), T, pos)\n//\n// Pos() returns the ast.CallExpr.Lparen, if the instruction arose\n// from an explicit conversion in the source.\n//\n// Example printed form:\n//\n//\tt2 = MakeInterface <interface{}> t1\ntype MakeInterface struct {\n\tregister\n\tX Value\n}\n\n// The MakeClosure instruction yields a closure value whose code is\n// Fn and whose free variables' values are supplied by Bindings.\n//\n// Type() returns a (possibly named) *types.Signature.\n//\n// Pos() returns the ast.FuncLit.Type.Func for a function literal\n// closure or the ast.SelectorExpr.Sel for a bound method closure.\n//\n// Example printed form:\n//\n//\tt1 = MakeClosure <func()> foo$1 t1 t2\n//\tt5 = MakeClosure <func(int)> (T).foo$bound t4\ntype MakeClosure struct {\n\tregister\n\tFn       Value   // always a *Function\n\tBindings []Value // values for each free variable in Fn.FreeVars\n}\n\n// The MakeMap instruction creates a new hash-table-based map object\n// and yields a value of kind map.\n//\n// Type() returns a (possibly named) *types.Map.\n//\n// Pos() returns the ast.CallExpr.Lparen, if created by make(map), or\n// the ast.CompositeLit.Lbrack if created by a literal.\n//\n// Example printed form:\n//\n//\tt1 = MakeMap <map[string]int>\n//\tt2 = MakeMap <StringIntMap> t1\ntype MakeMap struct {\n\tregister\n\tReserve Value // initial space reservation; nil => default\n}\n\n// The MakeChan instruction creates a new channel object and yields a\n// value of kind chan.\n//\n// Type() returns a (possibly named) *types.Chan.\n//\n// Pos() returns the ast.CallExpr.Lparen for the make(chan) that\n// created it.\n//\n// Example printed form:\n//\n//\tt3 = MakeChan <chan int> t1\n//\tt4 = MakeChan <chan IntChan> t2\ntype MakeChan struct {\n\tregister\n\tSize Value // int; size of buffer; zero => synchronous.\n}\n\n// The MakeSlice instruction yields a slice of length Len backed by a\n// newly allocated array of length Cap.\n//\n// Both Len and Cap must be non-nil Values of integer type.\n//\n// (Alloc(types.Array) followed by Slice will not suffice because\n// Alloc can only create arrays of constant length.)\n//\n// Type() returns a (possibly named) *types.Slice.\n//\n// Pos() returns the ast.CallExpr.Lparen for the make([]T) that\n// created it.\n//\n// Example printed form:\n//\n//\tt3 = MakeSlice <[]string> t1 t2\n//\tt4 = MakeSlice <StringSlice> t1 t2\ntype MakeSlice struct {\n\tregister\n\tLen Value\n\tCap Value\n}\n\n// The Slice instruction yields a slice of an existing string, slice\n// or *array X between optional integer bounds Low and High.\n//\n// Dynamically, this instruction panics if X evaluates to a nil *array\n// pointer.\n//\n// Type() returns string if the type of X was string, otherwise a\n// *types.Slice with the same element type as X.\n//\n// Pos() returns the ast.SliceExpr.Lbrack if created by a x[:] slice\n// operation, the ast.CompositeLit.Lbrace if created by a literal, or\n// NoPos if not explicit in the source (e.g. a variadic argument slice).\n//\n// Example printed form:\n//\n//\tt4 = Slice <[]int> t3 t2 t1 <nil>\ntype Slice struct {\n\tregister\n\tX              Value // slice, string, or *array\n\tLow, High, Max Value // each may be nil\n}\n\n// The FieldAddr instruction yields the address of Field of *struct X.\n//\n// The field is identified by its index within the field list of the\n// struct type of X.\n//\n// Dynamically, this instruction panics if X evaluates to a nil\n// pointer.\n//\n// Type() returns a (possibly named) *types.Pointer.\n//\n// Pos() returns the position of the ast.SelectorExpr.Sel for the\n// field, if explicit in the source.\n//\n// Example printed form:\n//\n//\tt2 = FieldAddr <*int> [0] (X) t1\ntype FieldAddr struct {\n\tregister\n\tX     Value // *struct\n\tField int   // field is X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct).Field(Field)\n}\n\n// The Field instruction yields the Field of struct X.\n//\n// The field is identified by its index within the field list of the\n// struct type of X; by using numeric indices we avoid ambiguity of\n// package-local identifiers and permit compact representations.\n//\n// Pos() returns the position of the ast.SelectorExpr.Sel for the\n// field, if explicit in the source.\n//\n// Example printed form:\n//\n//\tt2 = FieldAddr <int> [0] (X) t1\ntype Field struct {\n\tregister\n\tX     Value // struct\n\tField int   // index into X.Type().(*types.Struct).Fields\n}\n\n// The IndexAddr instruction yields the address of the element at\n// index Index of collection X.  Index is an integer expression.\n//\n// The elements of maps and strings are not addressable; use StringLookup, MapLookup or\n// MapUpdate instead.\n//\n// Dynamically, this instruction panics if X evaluates to a nil *array\n// pointer.\n//\n// Type() returns a (possibly named) *types.Pointer.\n//\n// Pos() returns the ast.IndexExpr.Lbrack for the index operation, if\n// explicit in the source.\n//\n// Example printed form:\n//\n//\tt3 = IndexAddr <*int> t2 t1\ntype IndexAddr struct {\n\tregister\n\tX     Value // slice or *array,\n\tIndex Value // numeric index\n}\n\n// The Index instruction yields element Index of array X.\n//\n// Pos() returns the ast.IndexExpr.Lbrack for the index operation, if\n// explicit in the source.\n//\n// Example printed form:\n//\n//\tt3 = Index <int> t2 t1\ntype Index struct {\n\tregister\n\tX     Value // array\n\tIndex Value // integer index\n}\n\n// The MapLookup instruction yields element Index of collection X, a map.\n//\n// If CommaOk, the result is a 2-tuple of the value above and a\n// boolean indicating the result of a map membership test for the key.\n// The components of the tuple are accessed using Extract.\n//\n// Pos() returns the ast.IndexExpr.Lbrack, if explicit in the source.\n//\n// Example printed form:\n//\n//\tt4 = MapLookup <string> t3 t1\n//\tt6 = MapLookup <(string, bool)> t3 t2\ntype MapLookup struct {\n\tregister\n\tX       Value // map\n\tIndex   Value // key-typed index\n\tCommaOk bool  // return a value,ok pair\n}\n\n// The StringLookup instruction yields element Index of collection X, a string.\n// Index is an integer expression.\n//\n// Pos() returns the ast.IndexExpr.Lbrack, if explicit in the source.\n//\n// Example printed form:\n//\n//\tt3 = StringLookup <uint8> t2 t1\ntype StringLookup struct {\n\tregister\n\tX     Value // string\n\tIndex Value // numeric index\n}\n\n// SelectState is a helper for Select.\n// It represents one goal state and its corresponding communication.\ntype SelectState struct {\n\tDir       types.ChanDir // direction of case (SendOnly or RecvOnly)\n\tChan      Value         // channel to use (for send or receive)\n\tSend      Value         // value to send (for send)\n\tPos       token.Pos     // position of token.ARROW\n\tDebugNode ast.Node      // ast.SendStmt or ast.UnaryExpr(<-) [debug mode]\n}\n\n// The Select instruction tests whether (or blocks until) one\n// of the specified sent or received states is entered.\n//\n// Let n be the number of States for which Dir==RECV and Tᵢ (0 ≤ i < n)\n// be the element type of each such state's Chan.\n// Select returns an n+2-tuple\n//\n//\t(index int, recvOk bool, r₀ T₀, ... rₙ-1 Tₙ-1)\n//\n// The tuple's components, described below, must be accessed via the\n// Extract instruction.\n//\n// If Blocking, select waits until exactly one state holds, i.e. a\n// channel becomes ready for the designated operation of sending or\n// receiving; select chooses one among the ready states\n// pseudorandomly, performs the send or receive operation, and sets\n// 'index' to the index of the chosen channel.\n//\n// If !Blocking, select doesn't block if no states hold; instead it\n// returns immediately with index equal to -1.\n//\n// If the chosen channel was used for a receive, the rᵢ component is\n// set to the received value, where i is the index of that state among\n// all n receive states; otherwise rᵢ has the zero value of type Tᵢ.\n// Note that the receive index i is not the same as the state\n// index index.\n//\n// The second component of the triple, recvOk, is a boolean whose value\n// is true iff the selected operation was a receive and the receive\n// successfully yielded a value.\n//\n// Pos() returns the ast.SelectStmt.Select.\n//\n// Example printed form:\n//\n//\tt6 = SelectNonBlocking <(index int, ok bool, int)> [<-t4, t5<-t1]\n//\tt11 = SelectBlocking <(index int, ok bool)> []\ntype Select struct {\n\tregister\n\tStates   []*SelectState\n\tBlocking bool\n}\n\n// The Range instruction yields an iterator over the domain and range\n// of X, which must be a string or map.\n//\n// Elements are accessed via Next.\n//\n// Type() returns an opaque and degenerate \"rangeIter\" type.\n//\n// Pos() returns the ast.RangeStmt.For.\n//\n// Example printed form:\n//\n//\tt2 = Range <iter> t1\ntype Range struct {\n\tregister\n\tX Value // string or map\n}\n\n// The Next instruction reads and advances the (map or string)\n// iterator Iter and returns a 3-tuple value (ok, k, v).  If the\n// iterator is not exhausted, ok is true and k and v are the next\n// elements of the domain and range, respectively.  Otherwise ok is\n// false and k and v are undefined.\n//\n// Components of the tuple are accessed using Extract.\n//\n// The IsString field distinguishes iterators over strings from those\n// over maps, as the Type() alone is insufficient: consider\n// map[int]rune.\n//\n// Type() returns a *types.Tuple for the triple (ok, k, v).\n// The types of k and/or v may be types.Invalid.\n//\n// Example printed form:\n//\n//\tt5 = Next <(ok bool, k int, v rune)> t2\n//\tt5 = Next <(ok bool, k invalid type, v invalid type)> t2\ntype Next struct {\n\tregister\n\tIter     Value\n\tIsString bool // true => string iterator; false => map iterator.\n}\n\n// The TypeAssert instruction tests whether interface value X has type\n// AssertedType.\n//\n// If !CommaOk, on success it returns v, the result of the conversion\n// (defined below); on failure it panics.\n//\n// If CommaOk: on success it returns a pair (v, true) where v is the\n// result of the conversion; on failure it returns (z, false) where z\n// is AssertedType's zero value.  The components of the pair must be\n// accessed using the Extract instruction.\n//\n// If AssertedType is a concrete type, TypeAssert checks whether the\n// dynamic type in interface X is equal to it, and if so, the result\n// of the conversion is a copy of the value in the interface.\n//\n// If AssertedType is an interface, TypeAssert checks whether the\n// dynamic type of the interface is assignable to it, and if so, the\n// result of the conversion is a copy of the interface value X.\n// If AssertedType is a superinterface of X.Type(), the operation will\n// fail iff the operand is nil.  (Contrast with ChangeInterface, which\n// performs no nil-check.)\n//\n// Type() reflects the actual type of the result, possibly a\n// 2-types.Tuple; AssertedType is the asserted type.\n//\n// Pos() returns the ast.CallExpr.Lparen if the instruction arose from\n// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the\n// instruction arose from an explicit e.(T) operation; or the\n// ast.CaseClause.Case if the instruction arose from a case of a\n// type-switch statement.\n//\n// Example printed form:\n//\n//\tt2 = TypeAssert <int> t1\n//\tt4 = TypeAssert <(value fmt.Stringer, ok bool)> t1\ntype TypeAssert struct {\n\tregister\n\tX            Value\n\tAssertedType types.Type\n\tCommaOk      bool\n}\n\n// The Extract instruction yields component Index of Tuple.\n//\n// This is used to access the results of instructions with multiple\n// return values, such as Call, TypeAssert, Next, Recv,\n// MapLookup and others.\n//\n// Example printed form:\n//\n//\tt7 = Extract <bool> [1] (ok) t4\ntype Extract struct {\n\tregister\n\tTuple Value\n\tIndex int\n}\n\n// Instructions executed for effect.  They do not yield a value. --------------------\n\n// The Jump instruction transfers control to the sole successor of its\n// owning block.\n//\n// A Jump must be the last instruction of its containing BasicBlock.\n//\n// Pos() returns NoPos.\n//\n// Example printed form:\n//\n//\tJump → b1\ntype Jump struct {\n\tanInstruction\n}\n\n// The Unreachable pseudo-instruction signals that execution cannot\n// continue after the preceding function call because it terminates\n// the process.\n//\n// The instruction acts as a control instruction, jumping to the exit\n// block. However, this jump will never execute.\n//\n// An Unreachable instruction must be the last instruction of its\n// containing BasicBlock.\n//\n// Example printed form:\n//\n//\tUnreachable → b1\ntype Unreachable struct {\n\tanInstruction\n}\n\n// The If instruction transfers control to one of the two successors\n// of its owning block, depending on the boolean Cond: the first if\n// true, the second if false.\n//\n// An If instruction must be the last instruction of its containing\n// BasicBlock.\n//\n// Pos() returns the *ast.IfStmt, if explicit in the source.\n//\n// Example printed form:\n//\n//\tIf t2 → b1 b2\ntype If struct {\n\tanInstruction\n\tCond Value\n}\n\ntype ConstantSwitch struct {\n\tanInstruction\n\tTag Value\n\t// Constant branch conditions. A nil Value denotes the (implicit\n\t// or explicit) default branch.\n\tConds []Value\n}\n\ntype TypeSwitch struct {\n\tregister\n\tTag   Value\n\tConds []types.Type\n}\n\n// The Return instruction returns values and control back to the calling\n// function.\n//\n// len(Results) is always equal to the number of results in the\n// function's signature.\n//\n// If len(Results) > 1, Return returns a tuple value with the specified\n// components which the caller must access using Extract instructions.\n//\n// There is no instruction to return a ready-made tuple like those\n// returned by a \"value,ok\"-mode TypeAssert, MapLookup or Recv or\n// a tail-call to a function with multiple result parameters.\n//\n// Return must be the last instruction of its containing BasicBlock.\n// Such a block has no successors.\n//\n// Pos() returns the ast.ReturnStmt.Return, if explicit in the source.\n//\n// Example printed form:\n//\n//\tReturn\n//\tReturn t1 t2\ntype Return struct {\n\tanInstruction\n\tResults []Value\n}\n\n// The RunDefers instruction pops and invokes the entire stack of\n// procedure calls pushed by Defer instructions in this function.\n//\n// It is legal to encounter multiple 'rundefers' instructions in a\n// single control-flow path through a function; this is useful in\n// the combined init() function, for example.\n//\n// Pos() returns NoPos.\n//\n// Example printed form:\n//\n//\tRunDefers\ntype RunDefers struct {\n\tanInstruction\n}\n\n// The Panic instruction initiates a panic with value X.\n//\n// A Panic instruction must be the last instruction of its containing\n// BasicBlock, which must have one successor, the exit block.\n//\n// NB: 'go panic(x)' and 'defer panic(x)' do not use this instruction;\n// they are treated as calls to a built-in function.\n//\n// Pos() returns the ast.CallExpr.Lparen if this panic was explicit\n// in the source.\n//\n// Example printed form:\n//\n//\tPanic t1\ntype Panic struct {\n\tanInstruction\n\tX Value // an interface{}\n}\n\n// The Go instruction creates a new goroutine and calls the specified\n// function within it.\n//\n// See CallCommon for generic function call documentation.\n//\n// Pos() returns the ast.GoStmt.Go.\n//\n// Example printed form:\n//\n//\tGo println t1\n//\tGo t3\n//\tGoInvoke t4.Bar t2\ntype Go struct {\n\tanInstruction\n\tCall CallCommon\n}\n\n// The Defer instruction pushes the specified call onto a stack of\n// functions to be called by a RunDefers instruction or by a panic.\n//\n// If _DeferStack != nil, it indicates the defer list that the defer is\n// added to. Defer list values come from the Builtin function\n// ssa:deferstack. Calls to ssa:deferstack() produces the defer stack\n// of the current function frame. _DeferStack allows for deferring into an\n// alternative function stack than the current function.\n//\n// See CallCommon for generic function call documentation.\n//\n// Pos() returns the ast.DeferStmt.Defer.\n//\n// Example printed form:\n//\n//\tDefer println t1\n//\tDefer t3\n//\tDeferInvoke t4.Bar t2\ntype Defer struct {\n\tanInstruction\n\tCall        CallCommon\n\t_DeferStack Value // stack (from ssa:deferstack() intrinsic) onto which this function is pushed\n\n\t// TODO: Exporting _DeferStack and possibly making _DeferStack != nil awaits proposal https://github.com/golang/go/issues/66601.\n}\n\n// The Send instruction sends X on channel Chan.\n//\n// Pos() returns the ast.SendStmt.Arrow, if explicit in the source.\n//\n// Example printed form:\n//\n//\tSend t2 t1\ntype Send struct {\n\tanInstruction\n\tChan, X Value\n}\n\n// The Recv instruction receives from channel Chan.\n//\n// If CommaOk, the result is a 2-tuple of the value above\n// and a boolean indicating the success of the receive.  The\n// components of the tuple are accessed using Extract.\n//\n// Pos() returns the ast.UnaryExpr.OpPos, if explicit in the source.\n// For receive operations implicit in ranging over a channel,\n// Pos() returns the ast.RangeStmt.For.\n//\n// Example printed form:\n//\n//\tt2 = Recv <int> t1\n//\tt3 = Recv <(int, bool)> t1\ntype Recv struct {\n\tregister\n\tChan    Value\n\tCommaOk bool\n}\n\n// The Store instruction stores Val at address Addr.\n// Stores can be of arbitrary types.\n//\n// Pos() returns the position of the source-level construct most closely\n// associated with the memory store operation.\n// Since implicit memory stores are numerous and varied and depend upon\n// implementation choices, the details are not specified.\n//\n// Example printed form:\n//\n//\tStore {int} t2 t1\ntype Store struct {\n\tanInstruction\n\tAddr Value\n\tVal  Value\n}\n\n// The BlankStore instruction is emitted for assignments to the blank\n// identifier.\n//\n// BlankStore is a pseudo-instruction: it has no dynamic effect.\n//\n// Pos() returns NoPos.\n//\n// Example printed form:\n//\n//\tBlankStore t1\ntype BlankStore struct {\n\tanInstruction\n\tVal Value\n}\n\n// The MapUpdate instruction updates the association of Map[Key] to\n// Value.\n//\n// Pos() returns the ast.KeyValueExpr.Colon or ast.IndexExpr.Lbrack,\n// if explicit in the source.\n//\n// Example printed form:\n//\n//\tMapUpdate t3 t1 t2\ntype MapUpdate struct {\n\tanInstruction\n\tMap   Value\n\tKey   Value\n\tValue Value\n}\n\n// A DebugRef instruction maps a source-level expression Expr to the\n// IR value X that represents the value (!IsAddr) or address (IsAddr)\n// of that expression.\n//\n// DebugRef is a pseudo-instruction: it has no dynamic effect.\n//\n// Pos() returns Expr.Pos(), the start position of the source-level\n// expression.  This is not the same as the \"designated\" token as\n// documented at Value.Pos(). e.g. CallExpr.Pos() does not return the\n// position of the (\"designated\") Lparen token.\n//\n// DebugRefs are generated only for functions built with debugging\n// enabled; see Package.SetDebugMode() and the GlobalDebug builder\n// mode flag.\n//\n// DebugRefs are not emitted for ast.Idents referring to constants or\n// predeclared identifiers, since they are trivial and numerous.\n// Nor are they emitted for ast.ParenExprs.\n//\n// (By representing these as instructions, rather than out-of-band,\n// consistency is maintained during transformation passes by the\n// ordinary SSA renaming machinery.)\n//\n// Example printed form:\n//\n//\t; *ast.CallExpr @ 102:9 is t5\n//\t; var x float64 @ 109:72 is x\n//\t; address of *ast.CompositeLit @ 216:10 is t0\ntype DebugRef struct {\n\tanInstruction\n\tExpr   ast.Expr     // the referring expression (never *ast.ParenExpr)\n\tobject types.Object // the identity of the source var/func\n\tIsAddr bool         // Expr is addressable and X is the address it denotes\n\tX      Value        // the value or address of Expr\n}\n\n// Embeddable mix-ins and helpers for common parts of other structs. -----------\n\n// register is a mix-in embedded by all IR values that are also\n// instructions, i.e. virtual registers, and provides a uniform\n// implementation of most of the Value interface: Value.Name() is a\n// numbered register (e.g. \"t0\"); the other methods are field accessors.\n//\n// Temporary names are automatically assigned to each register on\n// completion of building a function in IR form.\ntype register struct {\n\tanInstruction\n\ttyp       types.Type // type of virtual register\n\treferrers []Instruction\n}\n\ntype node struct {\n\tsource ast.Node\n\tid     ID\n}\n\nfunc (n *node) setID(id ID) { n.id = id }\nfunc (n node) ID() ID       { return n.id }\n\nfunc (n *node) setSource(source ast.Node) { n.source = source }\nfunc (n *node) Source() ast.Node          { return n.source }\n\nfunc (n *node) Pos() token.Pos {\n\tif n.source != nil {\n\t\treturn n.source.Pos()\n\t}\n\treturn token.NoPos\n}\n\n// anInstruction is a mix-in embedded by all Instructions.\n// It provides the implementations of the Block and setBlock methods.\ntype anInstruction struct {\n\tnode\n\tblock   *BasicBlock // the basic block of this instruction\n\tcomment string\n}\n\nfunc (instr anInstruction) Comment() string {\n\treturn instr.comment\n}\n\n// CallCommon is contained by Go, Defer and Call to hold the\n// common parts of a function or method call.\n//\n// Each CallCommon exists in one of two modes, function call and\n// interface method invocation, or \"call\" and \"invoke\" for short.\n//\n// 1. \"call\" mode: when Method is nil (!IsInvoke), a CallCommon\n// represents an ordinary function call of the value in Value,\n// which may be a *Builtin, a *Function or any other value of kind\n// 'func'.\n//\n// Value may be one of:\n//\n//\t(a) a *Function, indicating a statically dispatched call\n//\t    to a package-level function, an anonymous function, or\n//\t    a method of a named type.\n//\t(b) a *MakeClosure, indicating an immediately applied\n//\t    function literal with free variables.\n//\t(c) a *Builtin, indicating a statically dispatched call\n//\t    to a built-in function.\n//\t(d) any other value, indicating a dynamically dispatched\n//\t    function call.\n//\n// StaticCallee returns the identity of the callee in cases\n// (a) and (b), nil otherwise.\n//\n// Args contains the arguments to the call.  If Value is a method,\n// Args[0] contains the receiver parameter.\n//\n// Example printed form:\n//\n//\tt3 = Call <()> println t1 t2\n//\tGo t3\n//\tDefer t3\n//\n// 2. \"invoke\" mode: when Method is non-nil (IsInvoke), a CallCommon\n// represents a dynamically dispatched call to an interface method.\n// In this mode, Value is the interface value and Method is the\n// interface's abstract method.  Note: an abstract method may be\n// shared by multiple interfaces due to embedding; Value.Type()\n// provides the specific interface used for this call.\n//\n// Value is implicitly supplied to the concrete method implementation\n// as the receiver parameter; in other words, Args[0] holds not the\n// receiver but the first true argument.\n//\n// Example printed form:\n//\n//\tt6 = Invoke <string> t5.String\n//\tGoInvoke t4.Bar t2\n//\tDeferInvoke t4.Bar t2\n//\n// For all calls to variadic functions (Signature().Variadic()),\n// the last element of Args is a slice.\ntype CallCommon struct {\n\tValue    Value       // receiver (invoke mode) or func value (call mode)\n\tMethod   *types.Func // abstract method (invoke mode)\n\tArgs     []Value     // actual parameters (in static method call, includes receiver)\n\tTypeArgs []types.Type\n\tResults  Value\n}\n\n// IsInvoke returns true if this call has \"invoke\" (not \"call\") mode.\nfunc (c *CallCommon) IsInvoke() bool {\n\treturn c.Method != nil\n}\n\n// Signature returns the signature of the called function.\n//\n// For an \"invoke\"-mode call, the signature of the interface method is\n// returned.\n//\n// In either \"call\" or \"invoke\" mode, if the callee is a method, its\n// receiver is represented by sig.Recv, not sig.Params().At(0).\nfunc (c *CallCommon) Signature() *types.Signature {\n\tif c.Method != nil {\n\t\treturn c.Method.Type().(*types.Signature)\n\t}\n\treturn typeutil.CoreType(c.Value.Type()).(*types.Signature)\n}\n\n// StaticCallee returns the callee if this is a trivially static\n// \"call\"-mode call to a function.\nfunc (c *CallCommon) StaticCallee() *Function {\n\tswitch fn := c.Value.(type) {\n\tcase *Function:\n\t\treturn fn\n\tcase *MakeClosure:\n\t\treturn fn.Fn.(*Function)\n\t}\n\treturn nil\n}\n\n// Description returns a description of the mode of this call suitable\n// for a user interface, e.g., \"static method call\".\nfunc (c *CallCommon) Description() string {\n\tswitch fn := c.Value.(type) {\n\tcase *Builtin:\n\t\treturn \"built-in function call\"\n\tcase *MakeClosure:\n\t\treturn \"static function closure call\"\n\tcase *Function:\n\t\tif fn.Signature.Recv() != nil {\n\t\t\treturn \"static method call\"\n\t\t}\n\t\treturn \"static function call\"\n\t}\n\tif c.IsInvoke() {\n\t\treturn \"dynamic method call\" // (\"invoke\" mode)\n\t}\n\treturn \"dynamic function call\"\n}\n\n// The CallInstruction interface, implemented by *Go, *Defer and *Call,\n// exposes the common parts of function-calling instructions,\n// yet provides a way back to the Value defined by *Call alone.\ntype CallInstruction interface {\n\tInstruction\n\tCommon() *CallCommon // returns the common parts of the call\n\tValue() *Call\n}\n\nfunc (s *Call) Common() *CallCommon  { return &s.Call }\nfunc (s *Defer) Common() *CallCommon { return &s.Call }\nfunc (s *Go) Common() *CallCommon    { return &s.Call }\n\nfunc (s *Call) Value() *Call  { return s }\nfunc (s *Defer) Value() *Call { return nil }\nfunc (s *Go) Value() *Call    { return nil }\n\nfunc (v *Builtin) Type() types.Type        { return v.sig }\nfunc (v *Builtin) Name() string            { return v.name }\nfunc (*Builtin) Referrers() *[]Instruction { return nil }\nfunc (v *Builtin) Pos() token.Pos          { return token.NoPos }\nfunc (v *Builtin) Object() types.Object    { return types.Universe.Lookup(v.name) }\nfunc (v *Builtin) Parent() *Function       { return nil }\n\nfunc (v *FreeVar) Type() types.Type          { return v.typ }\nfunc (v *FreeVar) Name() string              { return v.name }\nfunc (v *FreeVar) Referrers() *[]Instruction { return &v.referrers }\nfunc (v *FreeVar) Parent() *Function         { return v.parent }\n\nfunc (v *Global) Type() types.Type                     { return v.typ }\nfunc (v *Global) Name() string                         { return v.name }\nfunc (v *Global) Parent() *Function                    { return nil }\nfunc (v *Global) Referrers() *[]Instruction            { return nil }\nfunc (v *Global) Token() token.Token                   { return token.VAR }\nfunc (v *Global) Object() types.Object                 { return v.object }\nfunc (v *Global) String() string                       { return v.RelString(nil) }\nfunc (v *Global) Package() *Package                    { return v.Pkg }\nfunc (v *Global) RelString(from *types.Package) string { return relString(v, from) }\n\nfunc (v *Function) Name() string       { return v.name }\nfunc (v *Function) Type() types.Type   { return v.Signature }\nfunc (v *Function) Token() token.Token { return token.FUNC }\nfunc (v *Function) Object() types.Object {\n\tif v.object != nil {\n\t\treturn types.Object(v.object)\n\t}\n\treturn nil\n}\nfunc (v *Function) String() string    { return v.RelString(nil) }\nfunc (v *Function) Package() *Package { return v.Pkg }\nfunc (v *Function) Parent() *Function { return v.parent }\nfunc (v *Function) Referrers() *[]Instruction {\n\tif v.parent != nil {\n\t\treturn &v.referrers\n\t}\n\treturn nil\n}\n\nfunc (v *Parameter) Object() types.Object { return v.object }\n\nfunc (v *Alloc) Type() types.Type          { return v.typ }\nfunc (v *Alloc) Referrers() *[]Instruction { return &v.referrers }\n\nfunc (v *register) Type() types.Type          { return v.typ }\nfunc (v *register) setType(typ types.Type)    { v.typ = typ }\nfunc (v *register) Name() string              { return fmt.Sprintf(\"t%d\", v.id) }\nfunc (v *register) Referrers() *[]Instruction { return &v.referrers }\n\nfunc (v *anInstruction) Parent() *Function          { return v.block.parent }\nfunc (v *anInstruction) Block() *BasicBlock         { return v.block }\nfunc (v *anInstruction) setBlock(block *BasicBlock) { v.block = block }\nfunc (v *anInstruction) Referrers() *[]Instruction  { return nil }\n\nfunc (t *Type) Name() string                         { return t.object.Name() }\nfunc (t *Type) Pos() token.Pos                       { return t.object.Pos() }\nfunc (t *Type) Type() types.Type                     { return t.object.Type() }\nfunc (t *Type) Token() token.Token                   { return token.TYPE }\nfunc (t *Type) Object() types.Object                 { return t.object }\nfunc (t *Type) String() string                       { return t.RelString(nil) }\nfunc (t *Type) Package() *Package                    { return t.pkg }\nfunc (t *Type) RelString(from *types.Package) string { return relString(t, from) }\n\nfunc (c *NamedConst) Name() string                         { return c.object.Name() }\nfunc (c *NamedConst) Pos() token.Pos                       { return c.object.Pos() }\nfunc (c *NamedConst) String() string                       { return c.RelString(nil) }\nfunc (c *NamedConst) Type() types.Type                     { return c.object.Type() }\nfunc (c *NamedConst) Token() token.Token                   { return token.CONST }\nfunc (c *NamedConst) Object() types.Object                 { return c.object }\nfunc (c *NamedConst) Package() *Package                    { return c.pkg }\nfunc (c *NamedConst) RelString(from *types.Package) string { return relString(c, from) }\n\n// Func returns the package-level function of the specified name,\n// or nil if not found.\nfunc (p *Package) Func(name string) (f *Function) {\n\tf, _ = p.Members[name].(*Function)\n\treturn\n}\n\n// Var returns the package-level variable of the specified name,\n// or nil if not found.\nfunc (p *Package) Var(name string) (g *Global) {\n\tg, _ = p.Members[name].(*Global)\n\treturn\n}\n\n// Const returns the package-level constant of the specified name,\n// or nil if not found.\nfunc (p *Package) Const(name string) (c *NamedConst) {\n\tc, _ = p.Members[name].(*NamedConst)\n\treturn\n}\n\n// Type returns the package-level type of the specified name,\n// or nil if not found.\nfunc (p *Package) Type(name string) (t *Type) {\n\tt, _ = p.Members[name].(*Type)\n\treturn\n}\n\nfunc (s *DebugRef) Pos() token.Pos { return s.Expr.Pos() }\n\n// Operands.\n\nfunc (v *Alloc) Operands(rands []*Value) []*Value {\n\treturn rands\n}\n\nfunc (v *BinOp) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X, &v.Y)\n}\n\nfunc (c *CallCommon) Operands(rands []*Value) []*Value {\n\trands = append(rands, &c.Value)\n\tfor i := range c.Args {\n\t\trands = append(rands, &c.Args[i])\n\t}\n\treturn rands\n}\n\nfunc (s *Go) Operands(rands []*Value) []*Value {\n\treturn s.Call.Operands(rands)\n}\n\nfunc (s *Call) Operands(rands []*Value) []*Value {\n\treturn s.Call.Operands(rands)\n}\n\nfunc (s *Defer) Operands(rands []*Value) []*Value {\n\treturn append(s.Call.Operands(rands), &s._DeferStack)\n}\n\nfunc (v *ChangeInterface) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (v *ChangeType) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (v *Convert) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (v *MultiConvert) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (v *SliceToArrayPointer) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (v *SliceToArray) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (s *DebugRef) Operands(rands []*Value) []*Value {\n\treturn append(rands, &s.X)\n}\n\nfunc (s *Copy) Operands(rands []*Value) []*Value {\n\treturn append(rands, &s.X)\n}\n\nfunc (v *Extract) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.Tuple)\n}\n\nfunc (v *Field) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (v *FieldAddr) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (s *If) Operands(rands []*Value) []*Value {\n\treturn append(rands, &s.Cond)\n}\n\nfunc (s *ConstantSwitch) Operands(rands []*Value) []*Value {\n\trands = append(rands, &s.Tag)\n\tfor i := range s.Conds {\n\t\trands = append(rands, &s.Conds[i])\n\t}\n\treturn rands\n}\n\nfunc (s *TypeSwitch) Operands(rands []*Value) []*Value {\n\trands = append(rands, &s.Tag)\n\treturn rands\n}\n\nfunc (v *Index) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X, &v.Index)\n}\n\nfunc (v *IndexAddr) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X, &v.Index)\n}\n\nfunc (*Jump) Operands(rands []*Value) []*Value {\n\treturn rands\n}\n\nfunc (*Unreachable) Operands(rands []*Value) []*Value {\n\treturn rands\n}\n\nfunc (v *MapLookup) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X, &v.Index)\n}\n\nfunc (v *StringLookup) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X, &v.Index)\n}\n\nfunc (v *MakeChan) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.Size)\n}\n\nfunc (v *MakeClosure) Operands(rands []*Value) []*Value {\n\trands = append(rands, &v.Fn)\n\tfor i := range v.Bindings {\n\t\trands = append(rands, &v.Bindings[i])\n\t}\n\treturn rands\n}\n\nfunc (v *MakeInterface) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (v *MakeMap) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.Reserve)\n}\n\nfunc (v *MakeSlice) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.Len, &v.Cap)\n}\n\nfunc (v *MapUpdate) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.Map, &v.Key, &v.Value)\n}\n\nfunc (v *Next) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.Iter)\n}\n\nfunc (s *Panic) Operands(rands []*Value) []*Value {\n\treturn append(rands, &s.X)\n}\n\nfunc (v *Sigma) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (v *Phi) Operands(rands []*Value) []*Value {\n\tfor i := range v.Edges {\n\t\trands = append(rands, &v.Edges[i])\n\t}\n\treturn rands\n}\n\nfunc (v *Range) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (s *Return) Operands(rands []*Value) []*Value {\n\tfor i := range s.Results {\n\t\trands = append(rands, &s.Results[i])\n\t}\n\treturn rands\n}\n\nfunc (*RunDefers) Operands(rands []*Value) []*Value {\n\treturn rands\n}\n\nfunc (v *Select) Operands(rands []*Value) []*Value {\n\tfor i := range v.States {\n\t\trands = append(rands, &v.States[i].Chan, &v.States[i].Send)\n\t}\n\treturn rands\n}\n\nfunc (s *Send) Operands(rands []*Value) []*Value {\n\treturn append(rands, &s.Chan, &s.X)\n}\n\nfunc (recv *Recv) Operands(rands []*Value) []*Value {\n\treturn append(rands, &recv.Chan)\n}\n\nfunc (v *Slice) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X, &v.Low, &v.High, &v.Max)\n}\n\nfunc (s *Store) Operands(rands []*Value) []*Value {\n\treturn append(rands, &s.Addr, &s.Val)\n}\n\nfunc (s *BlankStore) Operands(rands []*Value) []*Value {\n\treturn append(rands, &s.Val)\n}\n\nfunc (v *TypeAssert) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (v *UnOp) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (v *Load) Operands(rands []*Value) []*Value {\n\treturn append(rands, &v.X)\n}\n\nfunc (v *AggregateConst) Operands(rands []*Value) []*Value {\n\tfor i := range v.Values {\n\t\trands = append(rands, &v.Values[i])\n\t}\n\treturn rands\n}\n\nfunc (v *CompositeValue) Operands(rands []*Value) []*Value {\n\tfor i := range v.Values {\n\t\trands = append(rands, &v.Values[i])\n\t}\n\treturn rands\n}\n\n// Non-Instruction Values:\nfunc (v *Builtin) Operands(rands []*Value) []*Value      { return rands }\nfunc (v *FreeVar) Operands(rands []*Value) []*Value      { return rands }\nfunc (v *Const) Operands(rands []*Value) []*Value        { return rands }\nfunc (v *ArrayConst) Operands(rands []*Value) []*Value   { return rands }\nfunc (v *GenericConst) Operands(rands []*Value) []*Value { return rands }\nfunc (v *Function) Operands(rands []*Value) []*Value     { return rands }\nfunc (v *Global) Operands(rands []*Value) []*Value       { return rands }\nfunc (v *Parameter) Operands(rands []*Value) []*Value    { return rands }\n"
  },
  {
    "path": "go/ir/stdlib_test.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n//lint:file-ignore SA1019 go/ssa's test suite is built around the deprecated go/loader. We'll leave fixing that to upstream.\n\n// Incomplete source tree on Android.\n\n//go:build !android\n\npackage ir_test\n\n// This file runs the IR builder in sanity-checking mode on all\n// packages beneath $GOROOT and prints some summary information.\n//\n// Run with \"go test -cpu=8 to\" set GOMAXPROCS.\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\n\t\"golang.org/x/tools/go/packages\"\n)\n\nfunc TestStdlib(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping in short mode; too slow (golang.org/issue/14113)\")\n\t}\n\n\tcfg := &packages.Config{\n\t\tMode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedTypesSizes,\n\t}\n\tpkgs, err := packages.Load(cfg, \"std\")\n\tif err != nil {\n\t\tt.Fatalf(\"Load failed: %v\", err)\n\t}\n\tfor _, pkg := range pkgs {\n\t\tif len(pkg.Errors) != 0 {\n\t\t\tt.Fatalf(\"Load failed: %v\", pkg.Errors[0])\n\t\t}\n\t}\n\n\tvar mode ir.BuilderMode\n\tmode |= ir.SanityCheckFunctions\n\tmode |= ir.GlobalDebug\n\tprog, _ := irutil.Packages(pkgs, mode, nil)\n\tprog.Build()\n}\n"
  },
  {
    "path": "go/ir/testdata/objlookup.go",
    "content": "//go:build ignore\n// +build ignore\n\npackage main\n\n// This file is the input to TestObjValueLookup in source_test.go,\n// which ensures that each occurrence of an ident defining or\n// referring to a func, var or const object can be mapped to its\n// corresponding IR Value.\n//\n// For every reference to a var object, we use annotations in comments\n// to denote both the expected IR Value kind, and whether to expect\n// its value (x) or its address (&x).\n//\n// For const and func objects, the results don't vary by reference and\n// are always values not addresses, so no annotations are needed.  The\n// declaration is enough.\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\ntype J int\n\nfunc (*J) method() {}\n\nconst globalConst = 0\n\nvar globalVar int //@ ir(globalVar,\"&Global\")\n\nfunc globalFunc() {}\n\ntype I interface {\n\tinterfaceMethod()\n}\n\ntype S struct {\n\tx int //@ ir(x,\"nil\")\n}\n\nfunc main() {\n\tprint(globalVar) //@ ir(globalVar,\"Load\")\n\tglobalVar = 1    //@ ir(globalVar,\"Const\")\n\n\tvar v0 int = 1 //@ ir(v0,\"Const\") // simple local value spec\n\tif v0 > 0 {    //@ ir(v0,\"Const\")\n\t\tv0 = 2 //@ ir(v0,\"Const\")\n\t}\n\tprint(v0) //@ ir(v0,\"Phi\")\n\n\t// v1 is captured and thus implicitly address-taken.\n\tvar v1 int = 1         //@ ir(v1,\"Const\")\n\tv1 = 2                 //@ ir(v1,\"Const\")\n\tfmt.Println(v1)        //@ ir(v1,\"Const\")\n\tf := func(param int) { //@ ir(f,\"MakeClosure\"), ir(param,\"Parameter\")\n\t\tif y := 1; y > 0 { //@ ir(y,\"Const\")\n\t\t\tprint(v1, param) //@ ir(v1,\"Load\") /*load*/, ir(param,\"Sigma\")\n\t\t}\n\t\tparam = 2      //@ ir(param,\"Const\")\n\t\tprintln(param) //@ ir(param,\"Const\")\n\t}\n\tfmt.Println(v1) //@ ir(v1,\"Load\") // load\n\n\tf(0) //@ ir(f,\"MakeClosure\")\n\n\tvar v2 int //@ ir(v2,\"Const\") // implicitly zero-initialized local value spec\n\tprint(v2)  //@ ir(v2,\"Const\")\n\n\tm := make(map[string]int) //@ ir(m,\"MakeMap\")\n\n\t// Local value spec with multi-valued RHS:\n\tvar v3, v4 = m[\"\"] //@ ir(v3,\"Extract\"), ir(v4,\"Extract\"), ir(m,\"MakeMap\")\n\tprint(v3)          //@ ir(v3,\"Extract\")\n\tprint(v4)          //@ ir(v4,\"Extract\")\n\n\tv3++    //@ ir(v3,\"BinOp\") // assign with op\n\tv3 += 2 //@ ir(v3,\"BinOp\") // assign with op\n\n\tv5, v6 := false, \"\" //@ ir(v5,\"Const\"), ir(v6,\"Const\") // defining assignment\n\tprint(v5)           //@ ir(v5,\"Const\")\n\tprint(v6)           //@ ir(v6,\"Const\")\n\n\tvar v7 S    //@ ir(v7,\"&Alloc\")\n\tv7.x = 1    //@ ir(v7,\"&Alloc\"), ir(x,\"&FieldAddr\")\n\tprint(v7.x) //@ ir(v7,\"&Alloc\"), ir(x,\"&FieldAddr\")\n\n\tvar v8 [1]int //@ ir(v8,\"&Alloc\")\n\tv8[0] = 0     //@ ir(v8,\"&Alloc\")\n\tprint(v8[:])  //@ ir(v8,\"&Alloc\")\n\t_ = v8[0]     //@ ir(v8,\"&Alloc\")\n\t_ = v8[:][0]  //@ ir(v8,\"&Alloc\")\n\tv8ptr := &v8  //@ ir(v8ptr,\"Alloc\"), ir(v8,\"&Alloc\")\n\t_ = v8ptr[0]  //@ ir(v8ptr,\"Alloc\")\n\t_ = *v8ptr    //@ ir(v8ptr,\"Alloc\")\n\n\tv8a := make([]int, 1) //@ ir(v8a,\"Slice\")\n\tv8a[0] = 0            //@ ir(v8a,\"Slice\")\n\tprint(v8a[:])         //@ ir(v8a,\"Slice\")\n\n\tv9 := S{} //@ ir(v9,\"&Alloc\")\n\n\tv10 := &v9 //@ ir(v10,\"Alloc\"), ir(v9,\"&Alloc\")\n\t_ = v10    //@ ir(v10,\"Alloc\")\n\n\tvar v11 *J = nil //@ ir(v11,\"Const\")\n\tv11.method()     //@ ir(v11,\"Const\")\n\n\tvar v12 J    //@ ir(v12,\"&Alloc\")\n\tv12.method() //@ ir(v12,\"&Alloc\") // implicitly address-taken\n\n\t// NB, in the following, 'method' resolves to the *types.Func\n\t// of (*J).method, so it doesn't help us locate the specific\n\t// ir.Values here: a bound-method closure and a promotion\n\t// wrapper.\n\t_ = v11.method            //@ ir(v11,\"Const\")\n\t_ = (*struct{ J }).method //@ ir(J,\"nil\")\n\n\t// These vars are not optimised away.\n\tif false {\n\t\tv13 := 0     //@ ir(v13,\"Const\")\n\t\tprintln(v13) //@ ir(v13,\"Const\")\n\t}\n\n\tswitch x := 1; x { //@ ir(x,\"Const\")\n\tcase v0: //@ ir(v0,\"Phi\")\n\t}\n\n\tfor k, v := range m { //@ ir(k,\"Extract\"), ir(v,\"Extract\"), ir(m,\"MakeMap\")\n\t\t_ = k //@ ir(k,\"Extract\")\n\t\tv++   //@ ir(v,\"BinOp\")\n\t}\n\n\tif y := 0; y > 1 { //@ ir(y,\"Const\"), ir(y,\"Const\")\n\t}\n\n\tvar i interface{}      //@ ir(i,\"Const\") // nil interface\n\ti = 1                  //@ ir(i,\"MakeInterface\")\n\tswitch i := i.(type) { //@ ir(i,\"MakeInterface\"), ir(i,\"MakeInterface\")\n\tcase int:\n\t\tprintln(i) //@ ir(i,\"Extract\")\n\t}\n\n\tch := make(chan int) //@ ir(ch,\"MakeChan\")\n\tselect {\n\tcase x := <-ch: //@ ir(x,\"Recv\") /*receive*/, ir(ch,\"MakeChan\")\n\t\t_ = x //@ ir(x,\"Recv\")\n\t}\n\n\t// .Op is an inter-package FieldVal-selection.\n\tvar err os.PathError //@ ir(err,\"&Alloc\")\n\t_ = err.Op           //@ ir(err,\"&Alloc\"), ir(Op,\"&FieldAddr\")\n\t_ = &err.Op          //@ ir(err,\"&Alloc\"), ir(Op,\"&FieldAddr\")\n\n\t// Exercise corner-cases of lvalues vs rvalues.\n\t// (Guessing IsAddr from the 'pointerness' won't cut it here.)\n\ttype N *N\n\tvar n N    //@ ir(n,\"Const\")\n\tn1 := n    //@ ir(n1,\"Const\"), ir(n,\"Const\")\n\t_ = &n1    //@ ir(n1,\"&Alloc\") // make n1 escape right away, else our lifting is too good\n\tn2 := &n1  //@ ir(n2,\"Alloc\"), ir(n1,\"&Alloc\")\n\tn3 := *n2  //@ ir(n3,\"Load\"), ir(n2,\"Alloc\")\n\tn4 := **n3 //@ ir(n4,\"Load\"), ir(n3,\"Load\")\n\t_ = n4     //@ ir(n4,\"Load\")\n}\n"
  },
  {
    "path": "go/ir/testdata/valueforexpr.go",
    "content": "//go:build ignore\n// +build ignore\n\npackage main\n\n// This file is the input to TestValueForExpr in source_test.go, which\n// ensures that each expression e immediately following a /*@kind*/(x)\n// annotation, when passed to Function.ValueForExpr(e), returns a\n// non-nil Value of the same type as e and of kind 'kind'.\n\nfunc f(spilled, unspilled int) {\n\t_ = /*@Parameter*/ (spilled)\n\t_ = /*@Parameter*/ (unspilled)\n\t_ = /*@nil*/ (1 + 2) // (constant)\n\ti := 0\n\n\tf := func() (int, int) { return 0, 0 }\n\n\t(print( /*@BinOp*/ (i + 1)))\n\t_, _ = /*@Call*/ (f())\n\tch := /*@MakeChan*/ (make(chan int))\n\t/*@Recv*/ (<-ch)\n\tx := /*@Recv*/ (<-ch)\n\t_ = x\n\tselect {\n\tcase /*@Extract*/ (<-ch):\n\tcase x := /*@Extract*/ (<-ch):\n\t\t_ = x\n\t}\n\tdefer /*@Function*/ (func() {\n\t})()\n\tgo /*@Function*/ (func() {\n\t})()\n\ty := 0\n\tif true && /*@BinOp*/ (bool(y > 0)) {\n\t\ty = 1\n\t}\n\t_ = /*@Phi*/ (y)\n\tmap1 := /*@MakeMap*/ (make(map[string]string))\n\t_ = map1\n\t_ = /*@Slice*/ (make([]int, 0))\n\t_ = /*@MakeClosure*/ (func() { print(spilled) })\n\n\t_ = /*@Load*/ (spilled)\n\n\tsl := []int{}\n\t_ = /*@Slice*/ (sl[:0])\n\n\t_ = /*@Alloc*/ (new(int))\n\ttmp := /*@Alloc*/ (new(int))\n\t_ = tmp\n\tvar iface interface{}\n\t_ = /*@TypeAssert*/ (iface.(int))\n\t_ = /*@Load*/ (sl[0])\n\t_ = /*@IndexAddr*/ (&sl[0])\n\t_ = /*@Index*/ ([2]int{}[0])\n\tvar p *int\n\t_ = /*@Load*/ (*p)\n\n\t_ = /*@Load*/ (global)\n\t/*@Load*/ (global)[\"\"] = \"\"\n\t/*@Global*/ (global) = map[string]string{}\n\n\tvar local t\n\t/*UnOp*/ (local.x) = 1\n\n\t// Exercise corner-cases of lvalues vs rvalues.\n\ttype N *N\n\tvar n N\n\t/*@Const*/ (n) = /*@Const*/ (n)\n\t/*@ChangeType*/ (n) = /*@Alloc*/ (&n)\n\t/*@Load*/ (n) = /*@Load*/ (n)\n\t/*@Load*/ (n) = /*@Load*/ (*n)\n\t/*@Load*/ (n) = /*@Load*/ (**n)\n}\n\nfunc complit() {\n\t// Composite literals.\n\t// We get different results for\n\t// - composite literal as value (e.g. operand to print)\n\t// - composite literal initializer for addressable value\n\t// - composite literal value assigned to blank var\n\n\t// 1. Slices\n\tprint( /*@Slice*/ ([]int{}))\n\tprint( /*@Alloc*/ (&[]int{}))\n\tprint(& /*@Slice*/ ([]int{}))\n\n\tsl1 := /*@Slice*/ ([]int{})\n\tsl2 := /*@Alloc*/ (&[]int{})\n\tsl3 := & /*@Slice*/ ([]int{})\n\t_, _, _ = sl1, sl2, sl3\n\n\t_ = /*@Slice*/ ([]int{})\n\t_ = /*@Alloc*/ (& /*@Slice*/ ([]int{}))\n\t_ = & /*@Slice*/ ([]int{})\n\n\t// 2. Arrays\n\tprint( /*@ArrayConst*/ ([1]int{}))\n\tprint( /*@Alloc*/ (&[1]int{}))\n\tprint(& /*@Alloc*/ ([1]int{}))\n\n\tarr1 := /*@ArrayConst*/ ([1]int{})\n\tarr2 := /*@Alloc*/ (&[1]int{})\n\tarr3 := & /*@Alloc*/ ([1]int{})\n\t_, _, _ = arr1, arr2, arr3\n\n\t_ = /*@ArrayConst*/ ([1]int{})\n\t_ = /*@Alloc*/ (& /*@Alloc*/ ([1]int{}))\n\t_ = & /*@Alloc*/ ([1]int{})\n\n\t// 3. Maps\n\ttype M map[int]int\n\tprint( /*@MakeMap*/ (M{}))\n\tprint( /*@Alloc*/ (&M{}))\n\tprint(& /*@MakeMap*/ (M{}))\n\n\tm1 := /*@MakeMap*/ (M{})\n\tm2 := /*@Alloc*/ (&M{})\n\tm3 := & /*@MakeMap*/ (M{})\n\t_, _, _ = m1, m2, m3\n\n\t_ = /*@MakeMap*/ (M{})\n\t_ = /*@Alloc*/ (& /*@MakeMap*/ (M{}))\n\t_ = & /*@MakeMap*/ (M{})\n\n\t// 4. Structs\n\tprint( /*@AggregateConst*/ (struct{}{}))\n\tprint( /*@Alloc*/ (&struct{}{}))\n\tprint(& /*@Alloc*/ (struct{}{}))\n\n\ts1 := /*@AggregateConst*/ (struct{}{})\n\ts2 := /*@Alloc*/ (&struct{}{})\n\ts3 := & /*@Alloc*/ (struct{}{})\n\t_, _, _ = s1, s2, s3\n\n\t_ = /*@AggregateConst*/ (struct{}{})\n\t_ = /*@Alloc*/ (& /*@Alloc*/ (struct{}{}))\n\t_ = & /*@Alloc*/ (struct{}{})\n}\n\ntype t struct{ x int }\n\n// Ensure we can locate methods of named types.\nfunc (t) f(param int) {\n\t_ = /*@Parameter*/ (param)\n}\n\n// Ensure we can locate init functions.\nfunc init() {\n\tm := /*@MakeMap*/ (make(map[string]string))\n\t_ = m\n}\n\n// Ensure we can locate variables in initializer expressions.\nvar global = /*@MakeMap*/ (make(map[string]string))\n\ntype t1 struct {\n\tx int\n}\ntype t2 struct {\n\tx int `tag`\n}\n\nfunc main() {\n\tvar tv1 t1\n\tvar tv2 t2 = /*@ChangeType*/ (t2(tv1))\n\t_ = tv2\n}\n"
  },
  {
    "path": "go/ir/util.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file defines a number of miscellaneous utility functions.\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"io\"\n\t\"os\"\n\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/exp/typeparams\"\n)\n\n//// AST utilities\n\nfunc unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }\n\n// isBlankIdent returns true iff e is an Ident with name \"_\".\n// They have no associated types.Object, and thus no type.\nfunc isBlankIdent(e ast.Expr) bool {\n\tid, ok := e.(*ast.Ident)\n\treturn ok && id.Name == \"_\"\n}\n\n//// Type utilities.  Some of these belong in go/types.\n\n// isPointer returns true for types whose underlying type is a pointer,\n// and for type parameters whose core type is a pointer.\nfunc isPointer(typ types.Type) bool {\n\tif ctyp := typeutil.CoreType(typ); ctyp != nil {\n\t\t_, ok := ctyp.(*types.Pointer)\n\t\treturn ok\n\t}\n\t_, ok := typ.Underlying().(*types.Pointer)\n\treturn ok\n}\n\n// deref returns a pointer's element type; otherwise it returns typ.\nfunc deref(typ types.Type) types.Type {\n\torig := typ\n\ttyp = types.Unalias(typ)\n\n\tif t, ok := typ.(*types.TypeParam); ok {\n\t\tif ctyp := typeutil.CoreType(t); ctyp != nil {\n\t\t\t// This can happen, for example, with len(T) where T is a\n\t\t\t// type parameter whose core type is a pointer to array.\n\t\t\ttyp = ctyp\n\t\t}\n\t}\n\tif p, ok := typ.Underlying().(*types.Pointer); ok {\n\t\treturn p.Elem()\n\t}\n\treturn orig\n}\n\n// recvType returns the receiver type of method obj.\nfunc recvType(obj *types.Func) types.Type {\n\treturn obj.Type().(*types.Signature).Recv().Type()\n}\n\n// logStack prints the formatted \"start\" message to stderr and\n// returns a closure that prints the corresponding \"end\" message.\n// Call using 'defer logStack(...)()' to show builder stack on panic.\n// Don't forget trailing parens!\nfunc logStack(format string, args ...any) func() {\n\tmsg := fmt.Sprintf(format, args...)\n\tio.WriteString(os.Stderr, msg)\n\tio.WriteString(os.Stderr, \"\\n\")\n\treturn func() {\n\t\tio.WriteString(os.Stderr, msg)\n\t\tio.WriteString(os.Stderr, \" end\\n\")\n\t}\n}\n\n// newVar creates a 'var' for use in a types.Tuple.\nfunc newVar(name string, typ types.Type) *types.Var {\n\treturn types.NewParam(token.NoPos, nil, name, typ)\n}\n\n// anonVar creates an anonymous 'var' for use in a types.Tuple.\nfunc anonVar(typ types.Type) *types.Var {\n\treturn newVar(\"\", typ)\n}\n\nvar lenResults = types.NewTuple(anonVar(tInt))\n\n// makeLen returns the len builtin specialized to type func(T)int.\nfunc makeLen(T types.Type) *Builtin {\n\tlenParams := types.NewTuple(anonVar(T))\n\treturn &Builtin{\n\t\tname: \"len\",\n\t\tsig:  types.NewSignatureType(nil, nil, nil, lenParams, lenResults, false),\n\t}\n}\n\ntype StackMap struct {\n\tm []map[Value]Value\n}\n\nfunc (m *StackMap) Push() {\n\tm.m = append(m.m, map[Value]Value{})\n}\n\nfunc (m *StackMap) Pop() {\n\tm.m = m.m[:len(m.m)-1]\n}\n\nfunc (m *StackMap) Get(key Value) (Value, bool) {\n\tfor i := len(m.m) - 1; i >= 0; i-- {\n\t\tif v, ok := m.m[i][key]; ok {\n\t\t\treturn v, true\n\t\t}\n\t}\n\treturn nil, false\n}\n\nfunc (m *StackMap) Set(k Value, v Value) {\n\tm.m[len(m.m)-1][k] = v\n}\n\n// Unwrap recursively unwraps Sigma and Copy nodes.\nfunc Unwrap(v Value) Value {\n\tfor {\n\t\tswitch vv := v.(type) {\n\t\tcase *Sigma:\n\t\t\tv = vv.X\n\t\tcase *Copy:\n\t\t\tv = vv.X\n\t\tdefault:\n\t\t\treturn v\n\t\t}\n\t}\n}\n\nfunc assert(x bool) {\n\tif !x {\n\t\tpanic(\"failed assertion\")\n\t}\n}\n\n// BlockMap is a mapping from basic blocks (identified by their indices) to values.\ntype BlockMap[T any] []T\n\n// isBasic reports whether t is a basic type.\nfunc isBasic(t types.Type) bool {\n\t_, ok := t.(*types.Basic)\n\treturn ok\n}\n\n// isNonTypeParamInterface reports whether t is an interface type but not a type parameter.\nfunc isNonTypeParamInterface(t types.Type) bool {\n\treturn !typeparams.IsTypeParam(t) && types.IsInterface(t)\n}\n"
  },
  {
    "path": "go/ir/wrappers.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage ir\n\n// This file defines synthesis of Functions that delegate to declared\n// methods; they come in three kinds:\n//\n// (1) wrappers: methods that wrap declared methods, performing\n//     implicit pointer indirections and embedded field selections.\n//\n// (2) thunks: funcs that wrap declared methods.  Like wrappers,\n//     thunks perform indirections and field selections. The thunk's\n//     first parameter is used as the receiver for the method call.\n//\n// (3) bounds: funcs that wrap declared methods.  The bound's sole\n//     free variable, supplied by a closure, is used as the receiver\n//     for the method call.  No indirections or field selections are\n//     performed since they can be done before the call.\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n)\n\n// -- wrappers -----------------------------------------------------------\n\n// makeWrapper returns a synthetic method that delegates to the\n// declared method denoted by meth.Obj(), first performing any\n// necessary pointer indirections or field selections implied by meth.\n//\n// The resulting method's receiver type is meth.Recv().\n//\n// This function is versatile but quite subtle!  Consider the\n// following axes of variation when making changes:\n//   - optional receiver indirection\n//   - optional implicit field selections\n//   - meth.Obj() may denote a concrete or an interface method\n//   - the result may be a thunk or a wrapper.\n//\n// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)\nfunc makeWrapper(prog *Program, sel *types.Selection) *Function {\n\tobj := sel.Obj().(*types.Func)       // the declared function\n\tsig := sel.Type().(*types.Signature) // type of this wrapper\n\n\tvar recv *types.Var // wrapper's receiver or thunk's params[0]\n\tname := obj.Name()\n\tvar description Synthetic\n\tvar start int // first regular param\n\tif sel.Kind() == types.MethodExpr {\n\t\tname += \"$thunk\"\n\t\tdescription = SyntheticThunk\n\t\trecv = sig.Params().At(0)\n\t\tstart = 1\n\t} else {\n\t\tdescription = SyntheticWrapper\n\t\trecv = sig.Recv()\n\t}\n\n\tif prog.mode&LogSource != 0 {\n\t\tdefer logStack(\"make %s to (%s)\", description, recv.Type())()\n\t}\n\tfn := &Function{\n\t\tname:         name,\n\t\tmethod:       sel,\n\t\tobject:       obj,\n\t\tSignature:    sig,\n\t\tSynthetic:    description,\n\t\tProg:         prog,\n\t\tfunctionBody: new(functionBody),\n\t}\n\tfn.initHTML(prog.PrintFunc)\n\tfn.startBody()\n\tfn.addSpilledParam(recv, nil)\n\tcreateParams(fn, start)\n\n\tindices := sel.Index()\n\n\tvar v Value = fn.Locals[0] // spilled receiver\n\tif isPointer(sel.Recv()) {\n\t\tv = emitLoad(fn, v, nil)\n\n\t\t// For simple indirection wrappers, perform an informative nil-check:\n\t\t// \"value method (T).f called using nil *T pointer\"\n\t\tif len(indices) == 1 && !isPointer(recvType(obj)) {\n\t\t\tvar c Call\n\t\t\tc.Call.Value = &Builtin{\n\t\t\t\tname: \"ir:wrapnilchk\",\n\t\t\t\tsig: types.NewSignatureType(nil, nil, nil,\n\t\t\t\t\ttypes.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)),\n\t\t\t\t\ttypes.NewTuple(anonVar(sel.Recv())), false),\n\t\t\t}\n\t\t\tc.Call.Args = []Value{\n\t\t\t\tv,\n\t\t\t\temitConst(fn, stringConst(deref(sel.Recv()).String(), nil)),\n\t\t\t\temitConst(fn, stringConst(sel.Obj().Name(), nil)),\n\t\t\t}\n\t\t\tc.setType(v.Type())\n\t\t\tv = fn.emit(&c, nil)\n\t\t}\n\t}\n\n\t// Invariant: v is a pointer, either\n\t//   value of *A receiver param, or\n\t// address of  A spilled receiver.\n\n\t// We use pointer arithmetic (FieldAddr possibly followed by\n\t// Load) in preference to value extraction (Field possibly\n\t// preceded by Load).\n\n\tv = emitImplicitSelections(fn, v, indices[:len(indices)-1], nil)\n\n\t// Invariant: v is a pointer, either\n\t//   value of implicit *C field, or\n\t// address of implicit  C field.\n\n\tvar c Call\n\tif r := recvType(obj); !types.IsInterface(r) { // concrete method\n\t\tif !isPointer(r) {\n\t\t\tv = emitLoad(fn, v, nil)\n\t\t}\n\t\tc.Call.Value = prog.declaredFunc(obj)\n\t\tc.Call.Args = append(c.Call.Args, v)\n\t} else {\n\t\tc.Call.Method = obj\n\t\tc.Call.Value = emitLoad(fn, v, nil)\n\t}\n\tfor _, arg := range fn.Params[1:] {\n\t\tc.Call.Args = append(c.Call.Args, arg)\n\t}\n\temitTailCall(fn, &c, nil)\n\tfn.finishBody()\n\treturn fn\n}\n\n// createParams creates parameters for wrapper method fn based on its\n// Signature.Params, which do not include the receiver.\n// start is the index of the first regular parameter to use.\nfunc createParams(fn *Function, start int) {\n\ttparams := fn.Signature.Params()\n\tfor i, n := start, tparams.Len(); i < n; i++ {\n\t\tfn.addParamVar(tparams.At(i), nil)\n\t}\n}\n\n// -- bounds -----------------------------------------------------------\n\n// makeBound returns a bound method wrapper (or \"bound\"), a synthetic\n// function that delegates to a concrete or interface method denoted\n// by obj.  The resulting function has no receiver, but has one free\n// variable which will be used as the method's receiver in the\n// tail-call.\n//\n// Use MakeClosure with such a wrapper to construct a bound method\n// closure.  e.g.:\n//\n//\ttype T int          or:  type T interface { meth() }\n//\tfunc (t T) meth()\n//\tvar t T\n//\tf := t.meth\n//\tf() // calls t.meth()\n//\n// f is a closure of a synthetic wrapper defined as if by:\n//\n//\tf := func() { return t.meth() }\n//\n// Unlike makeWrapper, makeBound need perform no indirection or field\n// selections because that can be done before the closure is\n// constructed.\n//\n// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)\nfunc makeBound(prog *Program, obj *types.Func) *Function {\n\tprog.methodsMu.Lock()\n\tdefer prog.methodsMu.Unlock()\n\tif prog.mode&LogSource != 0 {\n\t\tdefer logStack(\"%s\", SyntheticBound)()\n\t}\n\tfn := &Function{\n\t\tname:         obj.Name() + \"$bound\",\n\t\tobject:       obj,\n\t\tSignature:    changeRecv(obj.Type().(*types.Signature), nil), // drop receiver\n\t\tSynthetic:    SyntheticBound,\n\t\tProg:         prog,\n\t\tfunctionBody: new(functionBody),\n\t}\n\tfn.initHTML(prog.PrintFunc)\n\n\tfv := &FreeVar{name: \"recv\", typ: recvType(obj), parent: fn}\n\tfn.FreeVars = []*FreeVar{fv}\n\tfn.startBody()\n\tcreateParams(fn, 0)\n\tvar c Call\n\n\tif !types.IsInterface(recvType(obj)) { // concrete\n\t\tc.Call.Value = prog.declaredFunc(obj)\n\t\tc.Call.Args = []Value{fv}\n\t} else {\n\t\tc.Call.Value = fv\n\t\tc.Call.Method = obj\n\t}\n\tfor _, arg := range fn.Params {\n\t\tc.Call.Args = append(c.Call.Args, arg)\n\t}\n\temitTailCall(fn, &c, nil)\n\tfn.finishBody()\n\treturn fn\n}\n\n// -- thunks -----------------------------------------------------------\n\n// makeThunk returns a thunk, a synthetic function that delegates to a\n// concrete or interface method denoted by sel.Obj().  The resulting\n// function has no receiver, but has an additional (first) regular\n// parameter.\n//\n// Precondition: sel.Kind() == types.MethodExpr.\n//\n//\ttype T int          or:  type T interface { meth() }\n//\tfunc (t T) meth()\n//\tf := T.meth\n//\tvar t T\n//\tf(t) // calls t.meth()\n//\n// f is a synthetic wrapper defined as if by:\n//\n//\tf := func(t T) { return t.meth() }\n//\n// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)\nfunc makeThunk(prog *Program, sel *types.Selection) *Function {\n\tif sel.Kind() != types.MethodExpr {\n\t\tpanic(sel)\n\t}\n\n\tprog.methodsMu.Lock()\n\tdefer prog.methodsMu.Unlock()\n\n\tfn := makeWrapper(prog, sel)\n\tif fn.Signature.Recv() != nil {\n\t\tpanic(fn) // unexpected receiver\n\t}\n\treturn fn\n}\n\nfunc changeRecv(s *types.Signature, recv *types.Var) *types.Signature {\n\treturn types.NewSignatureType(recv, nil, nil, s.Params(), s.Results(), s.Variadic())\n}\n\n// makeInstance creates a wrapper function with signature sig that calls the generic function fn.\n// If targs is not nil, fn is a function and targs describes the concrete type arguments.\n// If targs is nil, fn is a method and the type arguments are derived from the receiver.\nfunc makeInstance(prog *Program, fn *Function, sig *types.Signature, targs *types.TypeList) *Function {\n\tif sig.Recv() != nil {\n\t\tassert(targs == nil)\n\t\t// Methods don't have their own type parameters, but the receiver does\n\t\ttargs = types.Unalias(deref(sig.Recv().Type())).(*types.Named).TypeArgs()\n\t} else {\n\t\tassert(targs != nil)\n\t}\n\n\twrapper := fn.generics.At(targs)\n\tif wrapper != nil {\n\t\treturn wrapper\n\t}\n\n\tvar name string\n\tif sig.Recv() != nil {\n\t\tname = fn.name\n\t} else {\n\t\tname = fmt.Sprintf(\"%s$generic#%d\", fn.name, fn.generics.Len())\n\t}\n\tw := &Function{\n\t\tname:         name,\n\t\tobject:       fn.object,\n\t\tSignature:    sig,\n\t\tSynthetic:    SyntheticGeneric,\n\t\tProg:         prog,\n\t\tfunctionBody: new(functionBody),\n\t}\n\tw.initHTML(prog.PrintFunc)\n\tw.startBody()\n\tif sig.Recv() != nil {\n\t\tw.addParamVar(sig.Recv(), nil)\n\t}\n\tcreateParams(w, 0)\n\tvar c Call\n\tc.Call.Value = fn\n\ttresults := fn.Signature.Results()\n\tif tresults.Len() == 1 {\n\t\tc.typ = tresults.At(0).Type()\n\t} else {\n\t\tc.typ = tresults\n\t}\n\n\tchangeType := func(v Value, typ types.Type) Value {\n\t\tif types.Identical(v.Type(), typ) {\n\t\t\treturn v\n\t\t}\n\t\tvar c ChangeType\n\t\tc.X = v\n\t\tc.typ = typ\n\t\treturn w.emit(&c, nil)\n\t}\n\n\tfor i, arg := range w.Params {\n\t\tif sig.Recv() != nil {\n\t\t\tif i == 0 {\n\t\t\t\tc.Call.Args = append(c.Call.Args, changeType(w.Params[0], fn.Signature.Recv().Type()))\n\t\t\t} else {\n\t\t\t\tc.Call.Args = append(c.Call.Args, changeType(arg, fn.Signature.Params().At(i-1).Type()))\n\t\t\t}\n\t\t} else {\n\t\t\tc.Call.Args = append(c.Call.Args, changeType(arg, fn.Signature.Params().At(i).Type()))\n\t\t}\n\t}\n\tfor arg := range targs.Types() {\n\t\tc.Call.TypeArgs = append(c.Call.TypeArgs, arg)\n\t}\n\tresults := w.emit(&c, nil)\n\tvar ret Return\n\tswitch tresults.Len() {\n\tcase 0:\n\tcase 1:\n\t\tret.Results = []Value{changeType(results, sig.Results().At(0).Type())}\n\tdefault:\n\t\tfor i := 0; i < tresults.Len(); i++ {\n\t\t\tv := emitExtract(w, results, i, nil)\n\t\t\tret.Results = append(ret.Results, changeType(v, sig.Results().At(i).Type()))\n\t\t}\n\t}\n\n\tw.Exit = w.newBasicBlock(\"exit\")\n\temitJump(w, w.Exit, nil)\n\tw.currentBlock = w.Exit\n\tw.emit(&ret, nil)\n\tw.currentBlock = nil\n\n\tw.finishBody()\n\n\tfn.generics.Set(targs, w)\n\treturn w\n}\n"
  },
  {
    "path": "go/ir/write.go",
    "content": "package ir\n\nfunc NewJump(parent *BasicBlock) *Jump {\n\treturn &Jump{anInstruction{block: parent}}\n}\n"
  },
  {
    "path": "go/loader/hash.go",
    "content": "package loader\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/go/buildid\"\n\t\"honnef.co/go/tools/lintcmd/cache\"\n)\n\n// computeHash computes a package's hash. The hash is based on all Go\n// files that make up the package, as well as the hashes of imported\n// packages.\nfunc computeHash(c *cache.Cache, pkg *PackageSpec) (cache.ActionID, error) {\n\tkey := c.NewHash(\"package \" + pkg.PkgPath)\n\tfmt.Fprintf(key, \"goos %s goarch %s\\n\", runtime.GOOS, runtime.GOARCH)\n\tfmt.Fprintf(key, \"import %q\\n\", pkg.PkgPath)\n\n\t// Compute the hashes of all files making up the package. As an\n\t// optimization, we use the build ID that Go already computed for us,\n\t// because it is virtually identical to hashing all CompiledGoFiles. It is\n\t// also sensitive to the Go version declared in go.mod.\n\tsuccess := false\n\tif pkg.ExportFile != \"\" {\n\t\tid, err := getBuildid(pkg.ExportFile)\n\t\tif err == nil {\n\t\t\tif idx := strings.IndexRune(id, '/'); idx > -1 {\n\t\t\t\tfmt.Fprintf(key, \"files %s\\n\", id[:idx])\n\t\t\t\tsuccess = true\n\t\t\t}\n\t\t}\n\t}\n\tif !success {\n\t\tfor _, f := range pkg.CompiledGoFiles {\n\t\t\th, err := cache.FileHash(f)\n\t\t\tif err != nil {\n\t\t\t\treturn cache.ActionID{}, err\n\t\t\t}\n\t\t\tfmt.Fprintf(key, \"file %s %x\\n\", f, h)\n\t\t}\n\t\tif pkg.Module != nil && pkg.Module.GoMod != \"\" {\n\t\t\t// The go.mod file specifies the language version, which affects how\n\t\t\t// packages are analyzed.\n\t\t\th, err := cache.FileHash(pkg.Module.GoMod)\n\t\t\tif err != nil {\n\t\t\t\t// TODO(dh): this doesn't work for tests because the go.mod file doesn't\n\t\t\t\t// exist on disk and is instead provided via an overlay. However, we're\n\t\t\t\t// unlikely to get here in the first place, as reading the build ID from\n\t\t\t\t// the export file is likely to succeed.\n\t\t\t\treturn cache.ActionID{}, fmt.Errorf(\"couldn't hash go.mod: %w\", err)\n\t\t\t} else {\n\t\t\t\tfmt.Fprintf(key, \"file %s %x\\n\", pkg.Module.GoMod, h)\n\t\t\t}\n\t\t}\n\t}\n\n\timps := make([]*PackageSpec, 0, len(pkg.Imports))\n\tfor _, v := range pkg.Imports {\n\t\timps = append(imps, v)\n\t}\n\tsort.Slice(imps, func(i, j int) bool {\n\t\treturn imps[i].PkgPath < imps[j].PkgPath\n\t})\n\n\tfor _, dep := range imps {\n\t\tif dep.ExportFile == \"\" {\n\t\t\tfmt.Fprintf(key, \"import %s \\n\", dep.PkgPath)\n\t\t} else {\n\t\t\tid, err := getBuildid(dep.ExportFile)\n\t\t\tif err == nil {\n\t\t\t\tfmt.Fprintf(key, \"import %s %s\\n\", dep.PkgPath, id)\n\t\t\t} else {\n\t\t\t\tfh, err := cache.FileHash(dep.ExportFile)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn cache.ActionID{}, err\n\t\t\t\t}\n\t\t\t\tfmt.Fprintf(key, \"import %s %x\\n\", dep.PkgPath, fh)\n\t\t\t}\n\t\t}\n\t}\n\treturn key.Sum(), nil\n}\n\nvar buildidCache = map[string]string{}\n\nfunc getBuildid(f string) (string, error) {\n\tif h, ok := buildidCache[f]; ok {\n\t\treturn h, nil\n\t}\n\th, err := buildid.ReadFile(f)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tbuildidCache[f] = h\n\treturn h, nil\n}\n"
  },
  {
    "path": "go/loader/loader.go",
    "content": "package loader\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/build\"\n\t\"go/parser\"\n\t\"go/scanner\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"os\"\n\t\"time\"\n\n\t\"honnef.co/go/tools/config\"\n\t\"honnef.co/go/tools/lintcmd/cache\"\n\n\t\"golang.org/x/tools/go/gcexportdata\"\n\t\"golang.org/x/tools/go/packages\"\n)\n\nconst MaxFileSize = 50 * 1024 * 1024 // 50 MB\n\nvar errMaxFileSize = errors.New(\"file exceeds max file size\")\n\ntype PackageSpec struct {\n\tID      string\n\tName    string\n\tPkgPath string\n\t// Errors that occurred while building the import graph. These will\n\t// primarily be parse errors or failure to resolve imports, but\n\t// may also be other errors.\n\tErrors          []packages.Error\n\tGoFiles         []string\n\tCompiledGoFiles []string\n\tOtherFiles      []string\n\tExportFile      string\n\tImports         map[string]*PackageSpec\n\tTypesSizes      types.Sizes\n\tHash            cache.ActionID\n\tModule          *packages.Module\n\n\tConfig config.Config\n}\n\nfunc (spec *PackageSpec) String() string {\n\treturn spec.ID\n}\n\ntype Package struct {\n\t*PackageSpec\n\n\t// Errors that occurred while loading the package. These will\n\t// primarily be parse or type errors, but may also be lower-level\n\t// failures such as file-system ones.\n\tErrors    []packages.Error\n\tTypes     *types.Package\n\tFset      *token.FileSet\n\tSyntax    []*ast.File\n\tTypesInfo *types.Info\n}\n\n// Graph resolves patterns and returns packages with all the\n// information required to later load type information, and optionally\n// syntax trees.\n//\n// The provided config can set any setting with the exception of Mode.\nfunc Graph(c *cache.Cache, cfg *packages.Config, patterns ...string) ([]*PackageSpec, error) {\n\tvar dcfg packages.Config\n\tif cfg != nil {\n\t\tdcfg = *cfg\n\t}\n\tdcfg.Mode = packages.NeedName |\n\t\tpackages.NeedImports |\n\t\tpackages.NeedDeps |\n\t\tpackages.NeedExportFile |\n\t\tpackages.NeedFiles |\n\t\tpackages.NeedCompiledGoFiles |\n\t\tpackages.NeedTypesSizes |\n\t\tpackages.NeedModule\n\tpkgs, err := packages.Load(&dcfg, patterns...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tm := map[*packages.Package]*PackageSpec{}\n\tpackages.Visit(pkgs, nil, func(pkg *packages.Package) {\n\t\tspec := &PackageSpec{\n\t\t\tID:              pkg.ID,\n\t\t\tName:            pkg.Name,\n\t\t\tPkgPath:         pkg.PkgPath,\n\t\t\tErrors:          pkg.Errors,\n\t\t\tGoFiles:         pkg.GoFiles,\n\t\t\tCompiledGoFiles: pkg.CompiledGoFiles,\n\t\t\tOtherFiles:      pkg.OtherFiles,\n\t\t\tExportFile:      pkg.ExportFile,\n\t\t\tImports:         map[string]*PackageSpec{},\n\t\t\tTypesSizes:      pkg.TypesSizes,\n\t\t\tModule:          pkg.Module,\n\t\t}\n\t\tfor path, imp := range pkg.Imports {\n\t\t\tspec.Imports[path] = m[imp]\n\t\t}\n\t\tif cdir := config.Dir(pkg.GoFiles); cdir != \"\" {\n\t\t\tcfg, err := config.Load(cdir)\n\t\t\tif err != nil {\n\t\t\t\tspec.Errors = append(spec.Errors, convertError(err)...)\n\t\t\t}\n\t\t\tspec.Config = cfg\n\t\t} else {\n\t\t\tspec.Config = config.DefaultConfig\n\t\t}\n\t\tspec.Hash, err = computeHash(c, spec)\n\t\tif err != nil {\n\t\t\tspec.Errors = append(spec.Errors, convertError(err)...)\n\t\t}\n\t\tm[pkg] = spec\n\t})\n\tout := make([]*PackageSpec, 0, len(pkgs))\n\tfor _, pkg := range pkgs {\n\t\tif len(pkg.CompiledGoFiles) == 0 && len(pkg.Errors) == 0 && pkg.PkgPath != \"unsafe\" {\n\t\t\t// If a package consists only of test files, then\n\t\t\t// go/packages incorrectly(?) returns an empty package for\n\t\t\t// the non-test variant. Get rid of those packages. See\n\t\t\t// #646.\n\t\t\t//\n\t\t\t// Do not, however, skip packages that have errors. Those,\n\t\t\t// too, may have no files, but we want to print the\n\t\t\t// errors.\n\t\t\tcontinue\n\t\t}\n\t\tout = append(out, m[pkg])\n\t}\n\n\treturn out, nil\n}\n\ntype program struct {\n\tfset     *token.FileSet\n\tpackages map[string]*types.Package\n\toptions  *Options\n}\n\ntype Stats struct {\n\tSource time.Duration\n\tExport map[*PackageSpec]time.Duration\n}\n\ntype Options struct {\n\t// The Go language version to use for the type checker. If unset, or if set\n\t// to \"module\", it will default to the Go version specified in the module;\n\t// if there is no module, it will default to the version of Go the\n\t// executable was built with.\n\tGoVersion string\n}\n\n// Load loads the package described in spec. Imports will be loaded\n// from export data, while the package itself will be loaded from\n// source.\n//\n// An error will only be returned for system failures, such as failure\n// to read export data from disk. Syntax and type errors, among\n// others, will only populate the returned package's Errors field.\nfunc Load(spec *PackageSpec, opts *Options) (*Package, Stats, error) {\n\tif opts == nil {\n\t\topts = &Options{}\n\t}\n\tif opts.GoVersion == \"\" {\n\t\topts.GoVersion = \"module\"\n\t}\n\tprog := &program{\n\t\tfset:     token.NewFileSet(),\n\t\tpackages: map[string]*types.Package{},\n\t\toptions:  opts,\n\t}\n\n\tstats := Stats{\n\t\tExport: map[*PackageSpec]time.Duration{},\n\t}\n\tfor _, imp := range spec.Imports {\n\t\tif imp.PkgPath == \"unsafe\" {\n\t\t\tcontinue\n\t\t}\n\t\tt := time.Now()\n\t\t_, err := prog.loadFromExport(imp)\n\t\tstats.Export[imp] = time.Since(t)\n\t\tif err != nil {\n\t\t\treturn nil, stats, err\n\t\t}\n\t}\n\tt := time.Now()\n\tpkg, err := prog.loadFromSource(spec)\n\tif err == errMaxFileSize {\n\t\tpkg, err = prog.loadFromExport(spec)\n\t}\n\tstats.Source = time.Since(t)\n\treturn pkg, stats, err\n}\n\n// loadFromExport loads a package from export data.\nfunc (prog *program) loadFromExport(spec *PackageSpec) (*Package, error) {\n\t// log.Printf(\"Loading package %s from export\", spec)\n\tif spec.ExportFile == \"\" {\n\t\treturn nil, fmt.Errorf(\"no export data for %q\", spec.ID)\n\t}\n\tf, err := os.Open(spec.ExportFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\n\tr, err := gcexportdata.NewReader(f)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttpkg, err := gcexportdata.Read(r, prog.fset, prog.packages, spec.PkgPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpkg := &Package{\n\t\tPackageSpec: spec,\n\t\tTypes:       tpkg,\n\t\tFset:        prog.fset,\n\t}\n\t// runtime.SetFinalizer(pkg, func(pkg *Package) {\n\t// \tlog.Println(\"Unloading package\", pkg.PkgPath)\n\t// })\n\treturn pkg, nil\n}\n\n// loadFromSource loads a package from source. All of its dependencies\n// must have been loaded already.\nfunc (prog *program) loadFromSource(spec *PackageSpec) (*Package, error) {\n\tif len(spec.Errors) > 0 {\n\t\tpanic(\"LoadFromSource called on package with errors\")\n\t}\n\n\tpkg := &Package{\n\t\tPackageSpec: spec,\n\t\tTypes:       types.NewPackage(spec.PkgPath, spec.Name),\n\t\tSyntax:      make([]*ast.File, len(spec.CompiledGoFiles)),\n\t\tFset:        prog.fset,\n\t\tTypesInfo: &types.Info{\n\t\t\tTypes:        make(map[ast.Expr]types.TypeAndValue),\n\t\t\tDefs:         make(map[*ast.Ident]types.Object),\n\t\t\tUses:         make(map[*ast.Ident]types.Object),\n\t\t\tImplicits:    make(map[ast.Node]types.Object),\n\t\t\tScopes:       make(map[ast.Node]*types.Scope),\n\t\t\tSelections:   make(map[*ast.SelectorExpr]*types.Selection),\n\t\t\tInstances:    make(map[*ast.Ident]types.Instance),\n\t\t\tFileVersions: make(map[*ast.File]string),\n\t\t},\n\t}\n\t// runtime.SetFinalizer(pkg, func(pkg *Package) {\n\t// \tlog.Println(\"Unloading package\", pkg.PkgPath)\n\t// })\n\n\t// OPT(dh): many packages have few files, much fewer than there\n\t// are CPU cores. Additionally, parsing each individual file is\n\t// very fast. A naive parallel implementation of this loop won't\n\t// be faster, and tends to be slower due to extra scheduling,\n\t// bookkeeping and potentially false sharing of cache lines.\n\tfor i, file := range spec.CompiledGoFiles {\n\t\tf, err := os.Open(file)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfi, err := f.Stat()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif fi.Size() >= MaxFileSize {\n\t\t\treturn nil, errMaxFileSize\n\t\t}\n\t\taf, err := parser.ParseFile(prog.fset, file, f, parser.ParseComments|parser.SkipObjectResolution)\n\t\tf.Close()\n\t\tif err != nil {\n\t\t\tpkg.Errors = append(pkg.Errors, convertError(err)...)\n\t\t\treturn pkg, nil\n\t\t}\n\t\tpkg.Syntax[i] = af\n\t}\n\timporter := func(path string) (*types.Package, error) {\n\t\tif path == \"unsafe\" {\n\t\t\treturn types.Unsafe, nil\n\t\t}\n\t\tif path == \"C\" {\n\t\t\t// go/packages doesn't tell us that cgo preprocessing\n\t\t\t// failed. When we subsequently try to parse the package,\n\t\t\t// we'll encounter the raw C import.\n\t\t\treturn nil, errors.New(\"cgo preprocessing failed\")\n\t\t}\n\t\tispecpkg := spec.Imports[path]\n\t\tif ispecpkg == nil {\n\t\t\treturn nil, fmt.Errorf(\"trying to import %q in the context of %q returned nil PackageSpec\", path, spec)\n\t\t}\n\t\tipkg := prog.packages[ispecpkg.PkgPath]\n\t\tif ipkg == nil {\n\t\t\treturn nil, fmt.Errorf(\"trying to import %q (%q) in the context of %q returned nil PackageSpec\", ispecpkg.PkgPath, path, spec)\n\t\t}\n\t\treturn ipkg, nil\n\t}\n\ttc := &types.Config{\n\t\tImporter: importerFunc(importer),\n\t\tError: func(err error) {\n\t\t\tpkg.Errors = append(pkg.Errors, convertError(err)...)\n\t\t},\n\t}\n\tif prog.options.GoVersion == \"module\" {\n\t\tif spec.Module != nil && spec.Module.GoVersion != \"\" {\n\t\t\ttc.GoVersion = \"go\" + spec.Module.GoVersion\n\t\t} else {\n\t\t\ttags := build.Default.ReleaseTags\n\t\t\ttc.GoVersion = tags[len(tags)-1]\n\t\t}\n\t} else {\n\t\ttc.GoVersion = prog.options.GoVersion\n\t}\n\t// Note that the type-checker can return a non-nil error even though the Go\n\t// compiler has already successfully built this package (which is an\n\t// invariant of getting to this point), for example because of the Go\n\t// version passed to the type checker.\n\terr := types.NewChecker(tc, pkg.Fset, pkg.Types, pkg.TypesInfo).Files(pkg.Syntax)\n\treturn pkg, err\n}\n\nfunc convertError(err error) []packages.Error {\n\tvar errs []packages.Error\n\t// taken from go/packages\n\tswitch err := err.(type) {\n\tcase packages.Error:\n\t\t// from driver\n\t\terrs = append(errs, err)\n\n\tcase *os.PathError:\n\t\t// from parser\n\t\terrs = append(errs, packages.Error{\n\t\t\tPos:  err.Path + \":1\",\n\t\t\tMsg:  err.Err.Error(),\n\t\t\tKind: packages.ParseError,\n\t\t})\n\n\tcase scanner.ErrorList:\n\t\t// from parser\n\t\tfor _, err := range err {\n\t\t\terrs = append(errs, packages.Error{\n\t\t\t\tPos:  err.Pos.String(),\n\t\t\t\tMsg:  err.Msg,\n\t\t\t\tKind: packages.ParseError,\n\t\t\t})\n\t\t}\n\n\tcase types.Error:\n\t\t// from type checker\n\t\terrs = append(errs, packages.Error{\n\t\t\tPos:  err.Fset.Position(err.Pos).String(),\n\t\t\tMsg:  err.Msg,\n\t\t\tKind: packages.TypeError,\n\t\t})\n\n\tcase config.ParseError:\n\t\terrs = append(errs, packages.Error{\n\t\t\tPos:  fmt.Sprintf(\"%s:%d:%d\", err.Filename, err.Position.Line, err.Position.Col),\n\t\t\tMsg:  fmt.Sprintf(\"%s (last key parsed: %q)\", err.Message, err.LastKey),\n\t\t\tKind: packages.ParseError,\n\t\t})\n\tdefault:\n\t\terrs = append(errs, packages.Error{\n\t\t\tPos:  \"-\",\n\t\t\tMsg:  err.Error(),\n\t\t\tKind: packages.UnknownError,\n\t\t})\n\t}\n\treturn errs\n}\n\ntype importerFunc func(path string) (*types.Package, error)\n\nfunc (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }\n"
  },
  {
    "path": "go/types/typeutil/ext.go",
    "content": "package typeutil\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n)\n\ntype Iterator struct {\n\telem types.Type\n}\n\nfunc (t *Iterator) Underlying() types.Type { return t }\nfunc (t *Iterator) String() string         { return fmt.Sprintf(\"iterator(%s)\", t.elem) }\nfunc (t *Iterator) Elem() types.Type       { return t.elem }\n\nfunc NewIterator(elem types.Type) *Iterator {\n\treturn &Iterator{elem: elem}\n}\n\ntype DeferStack struct{}\n\nfunc (t *DeferStack) Underlying() types.Type { return t }\nfunc (t *DeferStack) String() string         { return \"deferStack\" }\n\nfunc NewDeferStack() *DeferStack {\n\treturn &DeferStack{}\n}\n"
  },
  {
    "path": "go/types/typeutil/typeparams.go",
    "content": "package typeutil\n\nimport (\n\t\"errors\"\n\t\"go/types\"\n\t\"slices\"\n\n\t\"golang.org/x/exp/typeparams\"\n)\n\ntype TypeSet struct {\n\tTerms []*types.Term\n\tempty bool\n}\n\nfunc NewTypeSet(typ types.Type) TypeSet {\n\tterms, err := typeparams.NormalTerms(typ)\n\tif err != nil {\n\t\tif errors.Is(err, typeparams.ErrEmptyTypeSet) {\n\t\t\treturn TypeSet{nil, true}\n\t\t} else {\n\t\t\t// We couldn't determine the type set. Assume it's all types.\n\t\t\treturn TypeSet{nil, false}\n\t\t}\n\t}\n\treturn TypeSet{terms, false}\n}\n\n// CoreType returns the type set's core type, or nil if it has none.\n// The function only looks at type terms and may thus return core types for some empty type sets, such as\n// 'interface { map[int]string; foo() }'\nfunc (ts TypeSet) CoreType() types.Type {\n\tif len(ts.Terms) == 0 {\n\t\t// Either the type set is empty, or it isn't constrained. Either way it doesn't have a core type.\n\t\treturn nil\n\t}\n\ttyp := ts.Terms[0].Type().Underlying()\n\tfor _, term := range ts.Terms[1:] {\n\t\tut := term.Type().Underlying()\n\t\tif types.Identical(typ, ut) {\n\t\t\tcontinue\n\t\t}\n\n\t\tch1, ok := typ.(*types.Chan)\n\t\tif !ok {\n\t\t\treturn nil\n\t\t}\n\t\tch2, ok := ut.(*types.Chan)\n\t\tif !ok {\n\t\t\treturn nil\n\t\t}\n\t\tif ch1.Dir() == types.SendRecv {\n\t\t\t// typ is currently a bidirectional channel. The term's type is either also bidirectional, or\n\t\t\t// unidirectional. Use the term's type.\n\t\t\ttyp = ut\n\t\t} else if ch2.Dir() == types.SendRecv {\n\t\t\t// typ is currently a unidirectional channel and the term's type is bidirectional, which means it has no\n\t\t\t// effect.\n\t\t\tcontinue\n\t\t} else if ch1.Dir() != ch2.Dir() {\n\t\t\t// typ is not bidirectional and typ and term disagree about the direction\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn typ\n}\n\n// CoreType is a wrapper for NewTypeSet(typ).CoreType()\nfunc CoreType(typ types.Type) types.Type {\n\treturn NewTypeSet(typ).CoreType()\n}\n\n// All calls fn for each term in the type set and reports whether all invocations returned true.\n// If the type set is empty or unconstrained, All immediately returns false.\nfunc (ts TypeSet) All(fn func(*types.Term) bool) bool {\n\tif len(ts.Terms) == 0 {\n\t\treturn false\n\t}\n\tfor _, term := range ts.Terms {\n\t\tif !fn(term) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Any calls fn for each term in the type set and reports whether any invocation returned true.\n// It stops after the first call that returned true.\nfunc (ts TypeSet) Any(fn func(*types.Term) bool) bool {\n\treturn slices.ContainsFunc(ts.Terms, fn)\n}\n\n// All is a wrapper for NewTypeSet(typ).All(fn).\nfunc All(typ types.Type, fn func(*types.Term) bool) bool {\n\treturn NewTypeSet(typ).All(fn)\n}\n\n// Any is a wrapper for NewTypeSet(typ).Any(fn).\nfunc Any(typ types.Type, fn func(*types.Term) bool) bool {\n\treturn NewTypeSet(typ).Any(fn)\n}\n\nfunc IsSlice(term *types.Term) bool {\n\t_, ok := term.Type().Underlying().(*types.Slice)\n\treturn ok\n}\n"
  },
  {
    "path": "go/types/typeutil/typeparams_test.go",
    "content": "//go:build go1.18\n\npackage typeutil\n\nimport (\n\t\"go/types\"\n\t\"testing\"\n)\n\nfunc simpleUnionIface(terms ...*types.Term) *types.Interface {\n\treturn types.NewInterfaceType(nil, []types.Type{types.NewUnion(terms)})\n}\n\nfunc newChannelIface(chans ...types.Type) *types.Interface {\n\tvar terms []*types.Term\n\tfor _, ch := range chans {\n\t\tterms = append(terms, types.NewTerm(false, ch))\n\t}\n\treturn simpleUnionIface(terms...)\n}\n\nfunc TestTypeSetCoreType(t *testing.T) {\n\tpkg := types.NewPackage(\"pkg\", \"pkg\")\n\tTInt := types.Universe.Lookup(\"int\").Type()\n\tTUint := types.Universe.Lookup(\"uint\").Type()\n\tTMyInt1 := types.NewNamed(types.NewTypeName(0, pkg, \"MyInt1\", nil), TInt, nil)\n\tTMyInt2 := types.NewNamed(types.NewTypeName(0, pkg, \"MyInt2\", nil), TInt, nil)\n\tTChanInt := types.NewChan(types.SendRecv, TInt)\n\tTChanIntRecv := types.NewChan(types.RecvOnly, TInt)\n\tTChanIntSend := types.NewChan(types.SendOnly, TInt)\n\tTNamedChanInt := types.NewNamed(types.NewTypeName(0, pkg, \"NamedChan\", nil), TChanInt, nil)\n\n\ttt := []struct {\n\t\tiface *types.Interface\n\t\twant  types.Type\n\t}{\n\t\t// same underlying type\n\t\t{\n\t\t\tsimpleUnionIface(types.NewTerm(false, TMyInt1), types.NewTerm(false, TMyInt2)),\n\t\t\ttypes.Universe.Lookup(\"int\").Type(),\n\t\t},\n\t\t// different underlying types\n\t\t{\n\t\t\tsimpleUnionIface(types.NewTerm(false, TInt), types.NewTerm(false, TUint)),\n\t\t\tnil,\n\t\t},\n\t\t// empty type set\n\t\t{\n\t\t\ttypes.NewInterfaceType(nil, []types.Type{\n\t\t\t\ttypes.NewUnion([]*types.Term{types.NewTerm(false, TInt)}),\n\t\t\t\ttypes.NewUnion([]*types.Term{types.NewTerm(false, TUint)}),\n\t\t\t}),\n\t\t\tnil,\n\t\t},\n\t}\n\tfor _, tc := range tt {\n\t\tts := NewTypeSet(tc.iface)\n\t\tif !types.Identical(ts.CoreType(), tc.want) {\n\t\t\tt.Errorf(\"CoreType(%s) = %s, want %s\", tc.iface, ts.CoreType(), tc.want)\n\t\t}\n\t}\n\n\ttt2 := []struct {\n\t\tiface *types.Interface\n\t\twant  types.ChanDir\n\t}{\n\t\t{\n\t\t\t// named sr + unnamed sr = sr\n\t\t\t// sr + sr = sr\n\t\t\tnewChannelIface(TNamedChanInt, TChanInt),\n\t\t\ttypes.SendRecv,\n\t\t},\n\t\t{\n\t\t\t// sr + sr = sr\n\t\t\tnewChannelIface(TChanInt, TChanInt),\n\t\t\ttypes.SendRecv,\n\t\t},\n\t\t{\n\t\t\t// s + s = s\n\t\t\tnewChannelIface(TChanIntSend, TChanIntSend),\n\t\t\ttypes.SendOnly,\n\t\t},\n\t\t{\n\t\t\t// r + r = r\n\t\t\tnewChannelIface(TChanIntRecv, TChanIntRecv),\n\t\t\ttypes.RecvOnly,\n\t\t},\n\t\t{\n\t\t\t// s + r = nil\n\t\t\tnewChannelIface(TChanIntSend, TChanIntRecv),\n\t\t\t-1,\n\t\t},\n\t\t{\n\t\t\t// sr + r = r\n\t\t\tnewChannelIface(TChanInt, TChanIntRecv),\n\t\t\ttypes.RecvOnly,\n\t\t},\n\t\t{\n\t\t\t// sr + s = s\n\t\t\tnewChannelIface(TChanInt, TChanIntSend),\n\t\t\ttypes.SendOnly,\n\t\t},\n\t}\n\tfor _, tc := range tt2 {\n\t\tts := NewTypeSet(tc.iface)\n\t\tcore := ts.CoreType()\n\t\tif (core == nil) != (tc.want == -1) {\n\t\t\tt.Errorf(\"CoreType(%s) = %s, want %d\", tc.iface, core, tc.want)\n\t\t}\n\t\tif core == nil {\n\t\t\tcontinue\n\t\t}\n\t\tdir := core.(*types.Chan).Dir()\n\t\tif dir != tc.want {\n\t\t\tt.Errorf(\"direction of %s is %d, want %d\", tc.iface, dir, tc.want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "go/types/typeutil/upstream.go",
    "content": "package typeutil\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\t_ \"unsafe\"\n\n\t\"golang.org/x/tools/go/types/typeutil\"\n)\n\ntype MethodSetCache = typeutil.MethodSetCache\ntype Hasher = typeutil.Hasher\n\nfunc Callee(info *types.Info, call *ast.CallExpr) types.Object {\n\treturn typeutil.Callee(info, call)\n}\n\nfunc IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {\n\treturn typeutil.IntuitiveMethodSet(T, msets)\n}\n\nfunc MakeHasher() Hasher {\n\treturn typeutil.MakeHasher()\n}\n\ntype Map[V any] struct {\n\tm typeutil.Map\n}\n\nfunc (m *Map[V]) Delete(key types.Type) bool { return m.m.Delete(key) }\nfunc (m *Map[V]) At(key types.Type) (V, bool) {\n\tv := m.m.At(key)\n\tif v == nil {\n\t\tvar zero V\n\t\treturn zero, false\n\t} else {\n\t\treturn v.(V), true\n\t}\n}\nfunc (m *Map[V]) Set(key types.Type, value V) { m.m.Set(key, value) }\nfunc (m *Map[V]) Len() int                    { return m.m.Len() }\nfunc (m *Map[V]) Iterate(f func(key types.Type, value V)) {\n\tff := func(key types.Type, value any) {\n\t\tf(key, value.(V))\n\t}\n\tm.m.Iterate(ff)\n\n}\nfunc (m *Map[V]) Keys() []types.Type          { return m.m.Keys() }\nfunc (m *Map[V]) String() string              { return m.m.String() }\nfunc (m *Map[V]) KeysString() string          { return m.m.KeysString() }\nfunc (m *Map[V]) SetHasher(h typeutil.Hasher) { m.m.SetHasher(h) }\n"
  },
  {
    "path": "go/types/typeutil/util.go",
    "content": "package typeutil\n\nimport (\n\t\"bytes\"\n\t\"go/types\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"golang.org/x/exp/typeparams\"\n)\n\nvar bufferPool = &sync.Pool{\n\tNew: func() any {\n\t\tbuf := bytes.NewBuffer(nil)\n\t\tbuf.Grow(64)\n\t\treturn buf\n\t},\n}\n\nfunc FuncName(f *types.Func) string {\n\t// We don't care about aliases in this function because we use FuncName to check calls\n\t// to known methods, and method receivers are determined by the method declaration,\n\t// not the call. Thus, even if a user does 'type Alias = *sync.Mutex' and calls\n\t// Alias.Lock, we'll still see it as (*sync.Mutex).Lock.\n\n\tbuf := bufferPool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tif f.Type() != nil {\n\t\tsig := f.Type().(*types.Signature)\n\t\tif recv := sig.Recv(); recv != nil {\n\t\t\tbuf.WriteByte('(')\n\t\t\tif _, ok := recv.Type().(*types.Interface); ok {\n\t\t\t\t// gcimporter creates abstract methods of\n\t\t\t\t// named interfaces using the interface type\n\t\t\t\t// (not the named type) as the receiver.\n\t\t\t\t// Don't print it in full.\n\t\t\t\tbuf.WriteString(\"interface\")\n\t\t\t} else {\n\t\t\t\ttypes.WriteType(buf, recv.Type(), nil)\n\t\t\t}\n\t\t\tbuf.WriteByte(')')\n\t\t\tbuf.WriteByte('.')\n\t\t} else if f.Pkg() != nil {\n\t\t\twritePackage(buf, f.Pkg())\n\t\t}\n\t}\n\tbuf.WriteString(f.Name())\n\ts := buf.String()\n\tbufferPool.Put(buf)\n\treturn s\n}\n\nfunc writePackage(buf *bytes.Buffer, pkg *types.Package) {\n\tif pkg == nil {\n\t\treturn\n\t}\n\ts := pkg.Path()\n\tif s != \"\" {\n\t\tbuf.WriteString(s)\n\t\tbuf.WriteByte('.')\n\t}\n}\n\n// Dereference returns a pointer's element type; otherwise it returns\n// T.\nfunc Dereference(T types.Type) types.Type {\n\tif p, ok := T.Underlying().(*types.Pointer); ok {\n\t\treturn p.Elem()\n\t}\n\treturn T\n}\n\n// DereferenceR returns a pointer's element type; otherwise it returns\n// T. If the element type is itself a pointer, DereferenceR will be\n// applied recursively.\nfunc DereferenceR(T types.Type) types.Type {\n\tif p, ok := T.Underlying().(*types.Pointer); ok {\n\t\treturn DereferenceR(p.Elem())\n\t}\n\treturn T\n}\n\nfunc IsObject(obj types.Object, name string) bool {\n\tvar path string\n\tif pkg := obj.Pkg(); pkg != nil {\n\t\tpath = pkg.Path() + \".\"\n\t}\n\treturn path+obj.Name() == name\n}\n\n// IsTypeName reports whether obj represents the qualified name. If obj is a type alias,\n// IsTypeName checks both the alias and the aliased type, if the aliased type has a type\n// name.\nfunc IsTypeName(obj *types.TypeName, name string) bool {\n\tvar qf string\n\tif idx := strings.LastIndex(name, \".\"); idx != -1 {\n\t\tqf = name[:idx]\n\t\tname = name[idx+1:]\n\t}\n\tif obj.Name() == name &&\n\t\t((qf == \"\" && obj.Pkg() == nil) || (obj.Pkg() != nil && obj.Pkg().Path() == qf)) {\n\t\treturn true\n\t}\n\n\tif !obj.IsAlias() {\n\t\treturn false\n\t}\n\n\t// FIXME(dh): we should peel away one layer of alias at a time; this is blocked on\n\t// github.com/golang/go/issues/66559\n\tif typ, ok := types.Unalias(obj.Type()).(interface{ Obj() *types.TypeName }); ok {\n\t\treturn IsTypeName(typ.Obj(), name)\n\t}\n\n\treturn false\n}\n\nfunc IsPointerToTypeWithName(typ types.Type, name string) bool {\n\tptr, ok := types.Unalias(typ).(*types.Pointer)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn IsTypeWithName(ptr.Elem(), name)\n}\n\n// IsTypeWithName reports whether typ represents a type with the qualified name, If typ is\n// a type alias, IsTypeWithName checks both the alias and the aliased type. The following\n// types can have names: Basic, Named, Alias.\nfunc IsTypeWithName(typ types.Type, name string) bool {\n\tswitch typ := typ.(type) {\n\tcase *types.Basic:\n\t\treturn typ.Name() == name\n\tcase *types.Named:\n\t\treturn IsTypeName(typ.Obj(), name)\n\tcase *types.Alias:\n\t\t// FIXME(dh): we should peel away one layer of alias at a time; this is blocked on\n\t\t// github.com/golang/go/issues/66559\n\n\t\t// IsTypeName already handles aliases to other aliases or named types; our\n\t\t// fallback is required for aliases to basic types.\n\t\treturn IsTypeName(typ.Obj(), name) || IsTypeWithName(types.Unalias(typ), name)\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// IsPointerLike returns true if type T is like a pointer. This returns true for all nillable types,\n// unsafe.Pointer, and type sets where at least one term is pointer-like.\nfunc IsPointerLike(T types.Type) bool {\n\tswitch T := T.Underlying().(type) {\n\tcase *types.Interface:\n\t\tif T.IsMethodSet() {\n\t\t\treturn true\n\t\t} else {\n\t\t\tterms, err := typeparams.NormalTerms(T)\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tfor _, term := range terms {\n\t\t\t\tif IsPointerLike(term.Type()) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\tcase *types.Chan, *types.Map, *types.Signature, *types.Pointer, *types.Slice:\n\t\treturn true\n\tcase *types.Basic:\n\t\treturn T.Kind() == types.UnsafePointer\n\t}\n\treturn false\n}\n\ntype Field struct {\n\tVar  *types.Var\n\tTag  string\n\tPath []int\n}\n\n// FlattenFields recursively flattens T and embedded structs,\n// returning a list of fields. If multiple fields with the same name\n// exist, all will be returned.\nfunc FlattenFields(T *types.Struct) []Field {\n\treturn flattenFields(T, nil, nil)\n}\n\nfunc flattenFields(T *types.Struct, path []int, seen map[types.Type]bool) []Field {\n\tif seen == nil {\n\t\tseen = map[types.Type]bool{}\n\t}\n\tif seen[T] {\n\t\treturn nil\n\t}\n\tseen[T] = true\n\tvar out []Field\n\tfor i := 0; i < T.NumFields(); i++ {\n\t\tfield := T.Field(i)\n\t\ttag := T.Tag(i)\n\t\tnp := append(path[:len(path):len(path)], i)\n\t\tif field.Anonymous() {\n\t\t\tif s, ok := Dereference(field.Type()).Underlying().(*types.Struct); ok {\n\t\t\t\tout = append(out, flattenFields(s, np, seen)...)\n\t\t\t} else {\n\t\t\t\tout = append(out, Field{field, tag, np})\n\t\t\t}\n\t\t} else {\n\t\t\tout = append(out, Field{field, tag, np})\n\t\t}\n\t}\n\treturn out\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module honnef.co/go/tools\n\ngo 1.25.0\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c\n\tgolang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa\n\tgolang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678\n\tgolang.org/x/sys v0.39.0\n\tgolang.org/x/tools v0.40.1-0.20260108161641-ca281cf95054\n\tgolang.org/x/tools/go/expect v0.1.1-deprecated\n)\n\nrequire (\n\tgolang.org/x/mod v0.31.0 // indirect\n\tgolang.org/x/sync v0.19.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=\ngithub.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngolang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=\ngolang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=\ngolang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=\ngolang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=\ngolang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=\ngolang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=\ngolang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/tools v0.40.1-0.20260108161641-ca281cf95054 h1:CHVDrNHx9ZoOrNN9kKWYIbT5Rj+WF2rlwPkhbQQ5V4U=\ngolang.org/x/tools v0.40.1-0.20260108161641-ca281cf95054/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=\ngolang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=\ngolang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=\n"
  },
  {
    "path": "internal/analysisinternal/typeindex/typeindex.go",
    "content": "// Copyright 2025 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package typeindex defines an analyzer that provides a\n// [golang.org/x/tools/internal/typesinternal/typeindex.Index].\n//\n// Like [golang.org/x/tools/go/analysis/passes/inspect], it is\n// intended to be used as a helper by other analyzers; it reports no\n// diagnostics of its own.\npackage typeindex\n\nimport (\n\t\"reflect\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n\t\"honnef.co/go/tools/internal/typesinternal/typeindex\"\n)\n\nvar Analyzer = &analysis.Analyzer{\n\tName: \"typeindex\",\n\tDoc:  \"indexes of type information for later passes\",\n\tURL:  \"https://pkg.go.dev/golang.org/x/tools/internal/analysisinternal/typeindex\",\n\tRun: func(pass *analysis.Pass) (any, error) {\n\t\tinspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)\n\t\treturn typeindex.New(inspect, pass.Pkg, pass.TypesInfo), nil\n\t},\n\tRunDespiteErrors: true,\n\tRequires:         []*analysis.Analyzer{inspect.Analyzer},\n\tResultType:       reflect.TypeFor[*typeindex.Index](),\n}\n"
  },
  {
    "path": "internal/cmd/ast-to-pattern/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"io\"\n\t\"os\"\n\n\t\"honnef.co/go/tools/pattern\"\n)\n\nfunc main() {\n\tsrc, err := io.ReadAll(os.Stdin)\n\tif err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t\tos.Exit(1)\n\t}\n\tfset := token.NewFileSet()\n\tnode, err := parseDetectingNode(fset, string(src))\n\tif err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t\tos.Exit(1)\n\t}\n\n\tif _, ok := node.(*ast.File); ok {\n\t\tfmt.Fprintln(os.Stderr, \"cannot convert entire file to Node\")\n\t\tos.Exit(1)\n\t}\n\tfmt.Println(pattern.ASTToNode(node))\n}\n"
  },
  {
    "path": "internal/cmd/ast-to-pattern/parse.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/scanner\"\n\t\"go/token\"\n\t\"text/template\"\n)\n\nvar tmplDecl = template.Must(template.New(\"\").Parse(`` +\n\t`package p; {{ . }}`))\n\nvar tmplExprs = template.Must(template.New(\"\").Parse(`` +\n\t`package p; var _ = []interface{}{ {{ . }}, }`))\n\nvar tmplStmts = template.Must(template.New(\"\").Parse(`` +\n\t`package p; func _() { {{ . }} }`))\n\nvar tmplType = template.Must(template.New(\"\").Parse(`` +\n\t`package p; var _ {{ . }}`))\n\nvar tmplValSpec = template.Must(template.New(\"\").Parse(`` +\n\t`package p; var {{ . }}`))\n\nfunc execTmpl(tmpl *template.Template, src string) string {\n\tvar buf bytes.Buffer\n\tif err := tmpl.Execute(&buf, src); err != nil {\n\t\tpanic(err)\n\t}\n\treturn buf.String()\n}\n\nfunc noBadNodes(node ast.Node) bool {\n\tany := false\n\tast.Inspect(node, func(n ast.Node) bool {\n\t\tif any {\n\t\t\treturn false\n\t\t}\n\t\tswitch n.(type) {\n\t\tcase *ast.BadExpr, *ast.BadDecl:\n\t\t\tany = true\n\t\t}\n\t\treturn true\n\t})\n\treturn !any\n}\n\nfunc parseType(fset *token.FileSet, src string) (ast.Expr, *ast.File, error) {\n\tasType := execTmpl(tmplType, src)\n\tf, err := parser.ParseFile(fset, \"\", asType, parser.SkipObjectResolution)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tvs := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec)\n\treturn vs.Type, f, nil\n}\n\n// parseDetectingNode tries its best to parse the ast.Node contained in src, as\n// one of: *ast.File, ast.Decl, ast.Expr, ast.Stmt, *ast.ValueSpec.\n// It also returns the *ast.File used for the parsing, so that the returned node\n// can be easily type-checked.\nfunc parseDetectingNode(fset *token.FileSet, src string) (any, error) {\n\tfile := fset.AddFile(\"\", fset.Base(), len(src))\n\tscan := scanner.Scanner{}\n\tscan.Init(file, []byte(src), nil, 0)\n\tif _, tok, _ := scan.Scan(); tok == token.EOF {\n\t\treturn nil, fmt.Errorf(\"empty source code\")\n\t}\n\tvar mainErr error\n\n\t// first try as a whole file\n\tif f, err := parser.ParseFile(fset, \"\", src, parser.SkipObjectResolution); err == nil && noBadNodes(f) {\n\t\treturn f, nil\n\t}\n\n\t// then as a single declaration, or many\n\tasDecl := execTmpl(tmplDecl, src)\n\tif f, err := parser.ParseFile(fset, \"\", asDecl, parser.SkipObjectResolution); err == nil && noBadNodes(f) {\n\t\tif len(f.Decls) == 1 {\n\t\t\treturn f.Decls[0], nil\n\t\t}\n\t\treturn f, nil\n\t}\n\n\t// then as value expressions\n\tasExprs := execTmpl(tmplExprs, src)\n\tif f, err := parser.ParseFile(fset, \"\", asExprs, parser.SkipObjectResolution); err == nil && noBadNodes(f) {\n\t\tvs := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec)\n\t\tcl := vs.Values[0].(*ast.CompositeLit)\n\t\tif len(cl.Elts) == 1 {\n\t\t\treturn cl.Elts[0], nil\n\t\t}\n\t\treturn cl.Elts, nil\n\t}\n\n\t// then try as statements\n\tasStmts := execTmpl(tmplStmts, src)\n\tif f, err := parser.ParseFile(fset, \"\", asStmts, parser.SkipObjectResolution); err == nil && noBadNodes(f) {\n\t\tbl := f.Decls[0].(*ast.FuncDecl).Body\n\t\tif len(bl.List) == 1 {\n\t\t\treturn bl.List[0], nil\n\t\t}\n\t\treturn bl.List, nil\n\t} else {\n\t\t// Statements is what covers most cases, so it will give\n\t\t// the best overall error message. Show positions\n\t\t// relative to where the user's code is put in the\n\t\t// template.\n\t\tmainErr = err\n\t}\n\n\t// type expressions not yet picked up, for e.g. chans and interfaces\n\tif typ, f, err := parseType(fset, src); err == nil && noBadNodes(f) {\n\t\treturn typ, nil\n\t}\n\n\t// value specs\n\tasValSpec := execTmpl(tmplValSpec, src)\n\tif f, err := parser.ParseFile(fset, \"\", asValSpec, parser.SkipObjectResolution); err == nil && noBadNodes(f) {\n\t\tvs := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec)\n\t\treturn vs, nil\n\t}\n\treturn nil, mainErr\n}\n"
  },
  {
    "path": "internal/cmd/gogrep/gogrep.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/format\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/pattern\"\n)\n\nfunc match(fset *token.FileSet, pat pattern.Pattern, f *ast.File) {\n\tentryNodesMap := make(map[reflect.Type]struct{})\n\tfor _, node := range pat.EntryNodes {\n\t\tentryNodesMap[reflect.TypeOf(node)] = struct{}{}\n\t}\n\tast.Inspect(f, func(node ast.Node) bool {\n\t\tif node == nil {\n\t\t\treturn true\n\t\t}\n\n\t\tif _, ok := entryNodesMap[reflect.TypeOf(node)]; ok {\n\t\t\tm := &pattern.Matcher{}\n\t\t\tif m.Match(pat, node) {\n\t\t\t\tfmt.Printf(\"%s: \", fset.Position(node.Pos()))\n\t\t\t\tformat.Node(os.Stdout, fset, node)\n\t\t\t\tfmt.Println()\n\t\t\t}\n\n\t\t\t// OPT(dh): we could further speed this up by not\n\t\t\t// chasing down impossible subtrees. For example,\n\t\t\t// we'll never find an ImportSpec beneath a FuncLit.\n\t\t}\n\t\treturn true\n\t})\n\n}\n\nfunc main() {\n\tflag.Parse()\n\t// XXX don't use MustParse, handle error\n\tp := &pattern.Parser{}\n\tq, err := p.Parse(flag.Args()[0])\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\tos.Exit(1)\n\t}\n\tdir := flag.Args()[1]\n\t// XXX should we create a new fileset per file? what if we're\n\t// checking millions of files, will this use up a lot of memory?\n\tfset := token.NewFileSet()\n\tfilepath.Walk(dir, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\t// XXX error handling\n\t\t\tpanic(err)\n\t\t}\n\t\tif !strings.HasSuffix(path, \".go\") {\n\t\t\treturn nil\n\t\t}\n\t\t// XXX don't try to parse irregular files or directories\n\t\tf, err := parser.ParseFile(fset, path, nil, parser.ParseComments)\n\t\tif err != nil {\n\t\t\t// XXX log error?\n\t\t\treturn nil\n\t\t}\n\n\t\tmatch(fset, q, f)\n\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "internal/cmd/irdump/main.go",
    "content": "// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// irdump: a tool for displaying the IR form of Go programs.\npackage main\n\nimport (\n\t\"flag\"\n\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis/singlechecker\"\n)\n\n// flags\nvar (\n\tdot  bool\n\thtml string\n)\n\nfunc init() {\n\tflag.BoolVar(&dot, \"dot\", false, \"Print Graphviz dot of CFG\")\n\tflag.StringVar(&html, \"html\", \"\", \"Print HTML for 'function'\")\n}\n\nfunc main() {\n\tbuildir.Debug.Mode = ir.PrintFunctions | ir.PrintPackages\n\tflag.Func(\"build\", ir.BuilderModeDoc, func(s string) error {\n\t\treturn buildir.Debug.Mode.Set(s)\n\t})\n\tsinglechecker.Main(buildir.Analyzer)\n\n\t// if dot {\n\t// \tfor _, p := range pkgs {\n\t// \t\tfor _, m := range p.Members {\n\t// \t\t\tif fn, ok := m.(*ir.Function); ok {\n\t// \t\t\t\tfmt.Println(\"digraph{\")\n\t// \t\t\t\tfmt.Printf(\"label = %q;\\n\", fn.Name())\n\t// \t\t\t\tfor _, b := range fn.Blocks {\n\t// \t\t\t\t\tfmt.Printf(\"n%d [label=\\\"%d: %s\\\"]\\n\", b.Index, b.Index, b.Comment)\n\t// \t\t\t\t\tfor _, succ := range b.Succs {\n\t// \t\t\t\t\t\tfmt.Printf(\"n%d -> n%d\\n\", b.Index, succ.Index)\n\t// \t\t\t\t\t}\n\t// \t\t\t\t}\n\t// \t\t\t\tfmt.Println(\"}\")\n\t// \t\t\t}\n\t// \t\t}\n\t// \t}\n\t// }\n}\n"
  },
  {
    "path": "internal/cmd/unused/unused.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t\"golang.org/x/tools/go/packages\"\n\t\"honnef.co/go/tools/go/loader\"\n\t\"honnef.co/go/tools/lintcmd/cache\"\n\t\"honnef.co/go/tools/unused\"\n)\n\n// OPT(dh): we don't need full graph merging if we're not flagging exported objects. In that case, we can reuse the old\n// list-based merging approach.\n\n// OPT(dh): we can either merge graphs as we process packages, or we can merge them all in one go afterwards (then\n// reloading them from cache). The first approach will likely lead to higher peak memory usage, but the latter may take\n// more wall time to finish if we had spare CPU resources while processing packages.\n\nfunc main() {\n\topts := unused.DefaultOptions\n\tflag.BoolVar(&opts.FieldWritesAreUses, \"field-writes-are-uses\", opts.FieldWritesAreUses, \"\")\n\tflag.BoolVar(&opts.PostStatementsAreReads, \"post-statements-are-reads\", opts.PostStatementsAreReads, \"\")\n\tflag.BoolVar(&opts.ExportedIsUsed, \"exported-is-used\", opts.ExportedIsUsed, \"\")\n\tflag.BoolVar(&opts.ExportedFieldsAreUsed, \"exported-fields-are-used\", opts.ExportedFieldsAreUsed, \"\")\n\tflag.BoolVar(&opts.ParametersAreUsed, \"parameters-are-used\", opts.ParametersAreUsed, \"\")\n\tflag.BoolVar(&opts.LocalVariablesAreUsed, \"local-variables-are-used\", opts.LocalVariablesAreUsed, \"\")\n\tflag.BoolVar(&opts.GeneratedIsUsed, \"generated-is-used\", opts.GeneratedIsUsed, \"\")\n\tflag.Parse()\n\n\t// pprof.StartCPUProfile(os.Stdout)\n\t// defer pprof.StopCPUProfile()\n\n\t// XXX set cache key for this tool\n\n\tc, err := cache.Default()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tcfg := &packages.Config{\n\t\tTests: true,\n\t}\n\tspecs, err := loader.Graph(c, cfg, flag.Args()...)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tvar sg unused.SerializedGraph\n\n\tourPkgs := map[string]struct{}{}\n\n\tfor _, spec := range specs {\n\t\tif len(spec.Errors) != 0 {\n\t\t\t// XXX priunt errors\n\t\t\tcontinue\n\t\t}\n\t\tlpkg, _, err := loader.Load(spec, nil)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif len(lpkg.Errors) != 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\t// XXX get directives and generated\n\t\tg := unused.Graph(lpkg.Fset, lpkg.Syntax, lpkg.Types, lpkg.TypesInfo, nil, nil, opts)\n\t\tsg.Merge(g)\n\t\tourPkgs[spec.PkgPath] = struct{}{}\n\t}\n\n\tres := sg.Results()\n\tfor _, obj := range res.Unused {\n\t\t// XXX format paths like staticcheck does\n\t\tif _, ok := ourPkgs[obj.Path.PkgPath]; !ok {\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"%s: %s %s is unused\\n\", obj.DisplayPosition, obj.Kind, obj.Name)\n\t}\n\n\tfmt.Fprintln(os.Stderr, sg.Dot())\n}\n"
  },
  {
    "path": "internal/diff/myers/diff.go",
    "content": "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// This code is a modified copy of code in gopls.\n// It's not as efficient as it could be, it allocates way too much, the API is sloppy.\n// But it's only used to print failed tests, so we don't care.\n\npackage myers\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// OpKind is used to denote the type of operation a line represents.\ntype OpKind int\n\nconst (\n\t// Delete is the operation kind for a line that is present in the input\n\t// but not in the output.\n\tDelete OpKind = iota\n\t// Insert is the operation kind for a line that is new in the output.\n\tInsert\n\t// Equal is the operation kind for a line that is the same in the input and\n\t// output, often used to provide context around edited lines.\n\tEqual\n)\n\n// String returns a human readable representation of an OpKind. It is not\n// intended for machine processing.\nfunc (k OpKind) String() string {\n\tswitch k {\n\tcase Delete:\n\t\treturn \"delete\"\n\tcase Insert:\n\t\treturn \"insert\"\n\tcase Equal:\n\t\treturn \"equal\"\n\tdefault:\n\t\tpanic(\"unknown operation kind\")\n\t}\n}\n\n// Sources:\n// https://blog.jcoglan.com/2017/02/17/the-myers-diff-algorithm-part-3/\n// https://www.codeproject.com/Articles/42279/%2FArticles%2F42279%2FInvestigating-Myers-diff-algorithm-Part-1-of-2\n\nfunc ComputeEdits(before, after string) []*operation {\n\tops := operations(splitLines(before), splitLines(after))\n\treturn ops\n}\n\ntype operation struct {\n\tKind    OpKind\n\tContent []string // content from b\n\tI1, I2  int      // indices of the line in a\n\tJ1      int      // indices of the line in b, J2 implied by len(Content)\n}\n\nfunc (op *operation) String() string {\n\tvar prefix string\n\tswitch op.Kind {\n\tcase Delete:\n\t\tprefix = \"-\"\n\tcase Insert:\n\t\tprefix = \"+\"\n\tcase Equal:\n\t\tprefix = \" \"\n\t}\n\tvar out strings.Builder\n\tfor _, line := range op.Content {\n\t\tout.WriteString(fmt.Sprintf(\"%s%s\", prefix, line))\n\t}\n\treturn out.String()\n}\n\n// operations returns the list of operations to convert a into b, consolidating\n// operations for multiple lines and not including equal lines.\nfunc operations(a, b []string) []*operation {\n\tif len(a) == 0 && len(b) == 0 {\n\t\treturn nil\n\t}\n\n\ttrace, offset := shortestEditSequence(a, b)\n\tsnakes := backtrack(trace, len(a), len(b), offset)\n\n\tM, N := len(a), len(b)\n\n\tvar i int\n\tsolution := make([]*operation, len(a)+len(b))\n\n\tadd := func(op *operation, i2, j2 int) {\n\t\tif op == nil {\n\t\t\treturn\n\t\t}\n\t\top.I2 = i2\n\t\tswitch op.Kind {\n\t\tcase Insert:\n\t\t\top.Content = b[op.J1:j2]\n\t\tcase Delete:\n\t\t\top.Content = a[op.I1:op.I2]\n\t\tcase Equal:\n\t\t\top.Content = a[op.I1:op.I2]\n\t\t}\n\t\tsolution[i] = op\n\t\ti++\n\t}\n\tx, y := 0, 0\n\tfor _, snake := range snakes {\n\t\tif len(snake) < 2 {\n\t\t\tcontinue\n\t\t}\n\t\tvar op *operation\n\t\t// delete (horizontal)\n\t\tfor snake[0]-snake[1] > x-y {\n\t\t\tif op == nil {\n\t\t\t\top = &operation{\n\t\t\t\t\tKind: Delete,\n\t\t\t\t\tI1:   x,\n\t\t\t\t\tJ1:   y,\n\t\t\t\t}\n\t\t\t}\n\t\t\tx++\n\t\t\tif x == M {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tadd(op, x, y)\n\t\top = nil\n\t\t// insert (vertical)\n\t\tfor snake[0]-snake[1] < x-y {\n\t\t\tif op == nil {\n\t\t\t\top = &operation{\n\t\t\t\t\tKind: Insert,\n\t\t\t\t\tI1:   x,\n\t\t\t\t\tJ1:   y,\n\t\t\t\t}\n\t\t\t}\n\t\t\ty++\n\t\t}\n\t\tadd(op, x, y)\n\t\top = nil\n\t\t// equal (diagonal)\n\t\tfor x < snake[0] {\n\t\t\tif op == nil {\n\t\t\t\top = &operation{\n\t\t\t\t\tKind: Equal,\n\t\t\t\t\tI1:   x,\n\t\t\t\t\tJ1:   y,\n\t\t\t\t}\n\t\t\t}\n\t\t\tx++\n\t\t\ty++\n\t\t}\n\t\tadd(op, x, y)\n\t\tif x >= M && y >= N {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn solution[:i]\n}\n\n// backtrack uses the trace for the edit sequence computation and returns the\n// \"snakes\" that make up the solution. A \"snake\" is a single deletion or\n// insertion followed by zero or diagonals.\nfunc backtrack(trace [][]int, x, y, offset int) [][]int {\n\tsnakes := make([][]int, len(trace))\n\td := len(trace) - 1\n\tfor ; x > 0 && y > 0 && d > 0; d-- {\n\t\tV := trace[d]\n\t\tif len(V) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tsnakes[d] = []int{x, y}\n\n\t\tk := x - y\n\n\t\tvar kPrev int\n\t\tif k == -d || (k != d && V[k-1+offset] < V[k+1+offset]) {\n\t\t\tkPrev = k + 1\n\t\t} else {\n\t\t\tkPrev = k - 1\n\t\t}\n\n\t\tx = V[kPrev+offset]\n\t\ty = x - kPrev\n\t}\n\tif x < 0 || y < 0 {\n\t\treturn snakes\n\t}\n\tsnakes[d] = []int{x, y}\n\treturn snakes\n}\n\n// shortestEditSequence returns the shortest edit sequence that converts a into b.\nfunc shortestEditSequence(a, b []string) ([][]int, int) {\n\tM, N := len(a), len(b)\n\tV := make([]int, 2*(N+M)+1)\n\toffset := N + M\n\ttrace := make([][]int, N+M+1)\n\n\t// Iterate through the maximum possible length of the SES (N+M).\n\tfor d := 0; d <= N+M; d++ {\n\t\tcopyV := make([]int, len(V))\n\t\t// k lines are represented by the equation y = x - k. We move in\n\t\t// increments of 2 because end points for even d are on even k lines.\n\t\tfor k := -d; k <= d; k += 2 {\n\t\t\t// At each point, we either go down or to the right. We go down if\n\t\t\t// k == -d, and we go to the right if k == d. We also prioritize\n\t\t\t// the maximum x value, because we prefer deletions to insertions.\n\t\t\tvar x int\n\t\t\tif k == -d || (k != d && V[k-1+offset] < V[k+1+offset]) {\n\t\t\t\tx = V[k+1+offset] // down\n\t\t\t} else {\n\t\t\t\tx = V[k-1+offset] + 1 // right\n\t\t\t}\n\n\t\t\ty := x - k\n\n\t\t\t// Diagonal moves while we have equal contents.\n\t\t\tfor x < M && y < N && a[x] == b[y] {\n\t\t\t\tx++\n\t\t\t\ty++\n\t\t\t}\n\n\t\t\tV[k+offset] = x\n\n\t\t\t// Return if we've exceeded the maximum values.\n\t\t\tif x == M && y == N {\n\t\t\t\t// Makes sure to save the state of the array before returning.\n\t\t\t\tcopy(copyV, V)\n\t\t\t\ttrace[d] = copyV\n\t\t\t\treturn trace, offset\n\t\t\t}\n\t\t}\n\n\t\t// Save the state of the array.\n\t\tcopy(copyV, V)\n\t\ttrace[d] = copyV\n\t}\n\treturn nil, 0\n}\n\nfunc splitLines(text string) []string {\n\tlines := strings.SplitAfter(text, \"\\n\")\n\tif lines[len(lines)-1] == \"\" {\n\t\tlines = lines[:len(lines)-1]\n\t}\n\treturn lines\n}\n"
  },
  {
    "path": "internal/passes/buildir/buildir.go",
    "content": "// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package buildir defines an Analyzer that constructs the IR\n// of an error-free package and returns the set of all\n// functions within it. It does not report any diagnostics itself but\n// may be used as an input to other analyzers.\n//\n// THIS INTERFACE IS EXPERIMENTAL AND MAY BE SUBJECT TO INCOMPATIBLE CHANGE.\npackage buildir\n\nimport (\n\t\"go/types\"\n\t\"reflect\"\n\n\t\"honnef.co/go/tools/go/ir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/ctrlflow\"\n)\n\nvar Debug = struct {\n\tMode ir.BuilderMode\n}{}\n\nvar Analyzer = &analysis.Analyzer{\n\tName:       \"buildir\",\n\tDoc:        \"build IR for later passes\",\n\tRun:        run,\n\tResultType: reflect.TypeFor[*IR](),\n\tRequires:   []*analysis.Analyzer{ctrlflow.Analyzer},\n}\n\n// IR provides intermediate representation for all the\n// source functions in the current package.\ntype IR struct {\n\tPkg      *ir.Package\n\tSrcFuncs []*ir.Function\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tcfgs := pass.ResultOf[ctrlflow.Analyzer].(*ctrlflow.CFGs)\n\n\t// Plundered from ssautil.BuildPackage.\n\n\t// We must create a new Program for each Package because the\n\t// analysis API provides no place to hang a Program shared by\n\t// all Packages. Consequently, IR Packages and Functions do not\n\t// have a canonical representation across an analysis session of\n\t// multiple packages. This is unlikely to be a problem in\n\t// practice because the analysis API essentially forces all\n\t// packages to be analysed independently, so any given call to\n\t// Analysis.Run on a package will see only IR objects belonging\n\t// to a single Program.\n\n\tmode := ir.GlobalDebug\n\tif Debug.Mode != 0 {\n\t\tmode = Debug.Mode\n\t}\n\n\tprog := ir.NewProgram(pass.Fset, mode)\n\n\tprog.SetNoReturn(cfgs.NoReturn)\n\n\t// Create IR packages for all imports.\n\t// Order is not significant.\n\tcreated := make(map[*types.Package]bool)\n\tvar createAll func(pkgs []*types.Package)\n\tcreateAll = func(pkgs []*types.Package) {\n\t\tfor _, p := range pkgs {\n\t\t\tif !created[p] {\n\t\t\t\tcreated[p] = true\n\t\t\t\tprog.CreatePackage(p, nil, nil, true)\n\t\t\t\tcreateAll(p.Imports())\n\t\t\t}\n\t\t}\n\t}\n\tcreateAll(pass.Pkg.Imports())\n\n\t// Create and build the primary package.\n\tirpkg := prog.CreatePackage(pass.Pkg, pass.Files, pass.TypesInfo, false)\n\tirpkg.Build()\n\n\t// Compute list of source functions, including literals,\n\t// in source order.\n\tvar addAnons func(f *ir.Function)\n\tfuncs := make([]*ir.Function, len(irpkg.Functions))\n\tcopy(funcs, irpkg.Functions)\n\taddAnons = func(f *ir.Function) {\n\t\tfor _, anon := range f.AnonFuncs {\n\t\t\tfuncs = append(funcs, anon)\n\t\t\taddAnons(anon)\n\t\t}\n\t}\n\tfor _, fn := range irpkg.Functions {\n\t\taddAnons(fn)\n\t}\n\n\treturn &IR{Pkg: irpkg, SrcFuncs: funcs}, nil\n}\n"
  },
  {
    "path": "internal/passes/buildir/buildir_test.go",
    "content": "// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage buildir_test\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"golang.org/x/tools/go/analysis/analysistest\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n)\n\nfunc Test(t *testing.T) {\n\tresult := analysistest.Run(t, analysistest.TestData(), buildir.Analyzer, \"a\")[0].Result\n\n\tirinfo := result.(*buildir.IR)\n\tgot := fmt.Sprint(irinfo.SrcFuncs)\n\twant := `[a.init a.Fib (a.T).fib a._ a._]`\n\tif got != want {\n\t\tt.Errorf(\"IR.SrcFuncs = %s, want %s\", got, want)\n\t\tfor _, f := range irinfo.SrcFuncs {\n\t\t\tf.WriteTo(os.Stderr)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/passes/buildir/testdata/src/a/a.go",
    "content": "package a\n\nfunc Fib(x int) int {\n\tif x < 2 {\n\t\treturn x\n\t}\n\treturn Fib(x-1) + Fib(x-2)\n}\n\ntype T int\n\nfunc (T) fib(x int) int { return Fib(x) }\n\nfunc _() {\n\tprint(\"hi\")\n}\n\nfunc _() {\n\tprint(\"hi\")\n}\n"
  },
  {
    "path": "internal/renameio/UPSTREAM",
    "content": "This package is a copy of cmd/go/internal/renameio.\nThe upstream package no longer exists, as the Go project replaced all of its uses with the lockedfile package.\n"
  },
  {
    "path": "internal/renameio/renameio.go",
    "content": "// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package renameio writes files atomically by renaming temporary files.\npackage renameio\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"math/rand\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\n\t\"honnef.co/go/tools/internal/robustio\"\n)\n\nconst patternSuffix = \".tmp\"\n\n// Pattern returns a glob pattern that matches the unrenamed temporary files\n// created when writing to filename.\nfunc Pattern(filename string) string {\n\treturn filepath.Join(filepath.Dir(filename), filepath.Base(filename)+patternSuffix)\n}\n\n// WriteFile is like ioutil.WriteFile, but first writes data to an arbitrary\n// file in the same directory as filename, then renames it atomically to the\n// final name.\n//\n// That ensures that the final location, if it exists, is always a complete file.\nfunc WriteFile(filename string, data []byte, perm os.FileMode) (err error) {\n\treturn WriteToFile(filename, bytes.NewReader(data), perm)\n}\n\n// WriteToFile is a variant of WriteFile that accepts the data as an io.Reader\n// instead of a slice.\nfunc WriteToFile(filename string, data io.Reader, perm os.FileMode) (err error) {\n\tf, err := tempFile(filepath.Dir(filename), filepath.Base(filename), perm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\t// Only call os.Remove on f.Name() if we failed to rename it: otherwise,\n\t\t// some other process may have created a new file with the same name after\n\t\t// that.\n\t\tif err != nil {\n\t\t\tf.Close()\n\t\t\tos.Remove(f.Name())\n\t\t}\n\t}()\n\n\tif _, err := io.Copy(f, data); err != nil {\n\t\treturn err\n\t}\n\t// Sync the file before renaming it: otherwise, after a crash the reader may\n\t// observe a 0-length file instead of the actual contents.\n\t// See https://golang.org/issue/22397#issuecomment-380831736.\n\tif err := f.Sync(); err != nil {\n\t\treturn err\n\t}\n\tif err := f.Close(); err != nil {\n\t\treturn err\n\t}\n\n\treturn robustio.Rename(f.Name(), filename)\n}\n\n// ReadFile is like ioutil.ReadFile, but on Windows retries spurious errors that\n// may occur if the file is concurrently replaced.\n//\n// Errors are classified heuristically and retries are bounded, so even this\n// function may occasionally return a spurious error on Windows.\n// If so, the error will likely wrap one of:\n//   - syscall.ERROR_ACCESS_DENIED\n//   - syscall.ERROR_FILE_NOT_FOUND\n//   - internal/syscall/windows.ERROR_SHARING_VIOLATION\nfunc ReadFile(filename string) ([]byte, error) {\n\treturn robustio.ReadFile(filename)\n}\n\n// tempFile creates a new temporary file with given permission bits.\nfunc tempFile(dir, prefix string, perm os.FileMode) (f *os.File, err error) {\n\tfor range 10000 {\n\t\tname := filepath.Join(dir, prefix+strconv.Itoa(rand.Intn(1000000000))+patternSuffix)\n\t\tf, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, perm)\n\t\tif os.IsExist(err) {\n\t\t\tcontinue\n\t\t}\n\t\tbreak\n\t}\n\treturn\n}\n"
  },
  {
    "path": "internal/renameio/renameio_test.go",
    "content": "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n//go:build !plan9\n\npackage renameio\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"math/rand\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"syscall\"\n\t\"testing\"\n\t\"time\"\n\n\t\"honnef.co/go/tools/internal/robustio\"\n)\n\nfunc TestConcurrentReadsAndWrites(t *testing.T) {\n\tdir := t.TempDir()\n\tpath := filepath.Join(dir, \"blob.bin\")\n\n\tconst chunkWords = 8 << 10\n\tbuf := make([]byte, 2*chunkWords*8)\n\tfor i := range uint64(2 * chunkWords) {\n\t\tbinary.LittleEndian.PutUint64(buf[i*8:], i)\n\t}\n\n\tvar attempts int64 = 128\n\tif !testing.Short() {\n\t\tattempts *= 16\n\t}\n\tconst parallel = 32\n\n\tvar sem = make(chan bool, parallel)\n\n\tvar (\n\t\twriteSuccesses, readSuccesses int64 // atomic\n\t\twriteErrnoSeen, readErrnoSeen sync.Map\n\t)\n\n\tfor n := attempts; n > 0; n-- {\n\t\tsem <- true\n\t\tgo func() {\n\t\t\tdefer func() { <-sem }()\n\n\t\t\ttime.Sleep(time.Duration(rand.Intn(100)) * time.Microsecond)\n\t\t\toffset := rand.Intn(chunkWords)\n\t\t\tchunk := buf[offset*8 : (offset+chunkWords)*8]\n\t\t\tif err := WriteFile(path, chunk, 0666); err == nil {\n\t\t\t\tatomic.AddInt64(&writeSuccesses, 1)\n\t\t\t} else if robustio.IsEphemeralError(err) {\n\t\t\t\tvar (\n\t\t\t\t\terrno syscall.Errno\n\t\t\t\t\tdup   bool\n\t\t\t\t)\n\t\t\t\tif errors.As(err, &errno) {\n\t\t\t\t\t_, dup = writeErrnoSeen.LoadOrStore(errno, true)\n\t\t\t\t}\n\t\t\t\tif !dup {\n\t\t\t\t\tt.Logf(\"ephemeral error: %v\", err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tt.Errorf(\"unexpected error: %v\", err)\n\t\t\t}\n\n\t\t\ttime.Sleep(time.Duration(rand.Intn(100)) * time.Microsecond)\n\t\t\tdata, err := ReadFile(path)\n\t\t\tif err == nil {\n\t\t\t\tatomic.AddInt64(&readSuccesses, 1)\n\t\t\t} else if robustio.IsEphemeralError(err) {\n\t\t\t\tvar (\n\t\t\t\t\terrno syscall.Errno\n\t\t\t\t\tdup   bool\n\t\t\t\t)\n\t\t\t\tif errors.As(err, &errno) {\n\t\t\t\t\t_, dup = readErrnoSeen.LoadOrStore(errno, true)\n\t\t\t\t}\n\t\t\t\tif !dup {\n\t\t\t\t\tt.Logf(\"ephemeral error: %v\", err)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\tt.Errorf(\"unexpected error: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif len(data) != 8*chunkWords {\n\t\t\t\tt.Errorf(\"read %d bytes, but each write is a %d-byte file\", len(data), 8*chunkWords)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tu := binary.LittleEndian.Uint64(data)\n\t\t\tfor i := 1; i < chunkWords; i++ {\n\t\t\t\tnext := binary.LittleEndian.Uint64(data[i*8:])\n\t\t\t\tif next != u+1 {\n\t\t\t\t\tt.Errorf(\"wrote sequential integers, but read integer out of sequence at offset %d\", i)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tu = next\n\t\t\t}\n\t\t}()\n\t}\n\n\tfor n := parallel; n > 0; n-- {\n\t\tsem <- true\n\t}\n\n\tvar minWriteSuccesses int64 = attempts\n\tif runtime.GOOS == \"windows\" {\n\t\t// Windows produces frequent \"Access is denied\" errors under heavy rename load.\n\t\t// As long as those are the only errors and *some* of the writes succeed, we're happy.\n\t\tminWriteSuccesses = attempts / 4\n\t}\n\n\tif writeSuccesses < minWriteSuccesses {\n\t\tt.Errorf(\"%d (of %d) writes succeeded; want ≥ %d\", writeSuccesses, attempts, minWriteSuccesses)\n\t} else {\n\t\tt.Logf(\"%d (of %d) writes succeeded (ok: ≥ %d)\", writeSuccesses, attempts, minWriteSuccesses)\n\t}\n\n\tvar minReadSuccesses int64 = attempts\n\n\tswitch runtime.GOOS {\n\tcase \"windows\":\n\t\t// Windows produces frequent \"Access is denied\" errors under heavy rename load.\n\t\t// As long as those are the only errors and *some* of the reads succeed, we're happy.\n\t\tminReadSuccesses = attempts / 4\n\n\tcase \"darwin\":\n\t\t// The filesystem on macOS 10.14 occasionally fails with \"no such file or\n\t\t// directory\" errors. See https://golang.org/issue/33041. The flake rate is\n\t\t// fairly low, so ensure that at least 75% of attempts succeed.\n\t\tminReadSuccesses = attempts - (attempts / 4)\n\t}\n\n\tif readSuccesses < minReadSuccesses {\n\t\tt.Errorf(\"%d (of %d) reads succeeded; want ≥ %d\", readSuccesses, attempts, minReadSuccesses)\n\t} else {\n\t\tt.Logf(\"%d (of %d) reads succeeded (ok: ≥ %d)\", readSuccesses, attempts, minReadSuccesses)\n\t}\n}\n"
  },
  {
    "path": "internal/renameio/umask_test.go",
    "content": "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n//go:build !plan9 && !windows && !js\n\npackage renameio\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"syscall\"\n\t\"testing\"\n)\n\nfunc TestWriteFileModeAppliesUmask(t *testing.T) {\n\tdir := t.TempDir()\n\n\tconst mode = 0644\n\tconst umask = 0007\n\tdefer syscall.Umask(syscall.Umask(umask))\n\n\tfile := filepath.Join(dir, \"testWrite\")\n\terr := WriteFile(file, []byte(\"go-build\"), mode)\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to write file: %v\", err)\n\t}\n\n\tfi, err := os.Stat(file)\n\tif err != nil {\n\t\tt.Fatalf(\"Stat %q (looking for mode %#o): %s\", file, mode, err)\n\t}\n\n\tif fi.Mode()&os.ModePerm != 0640 {\n\t\tt.Errorf(\"Stat %q: mode %#o want %#o\", file, fi.Mode()&os.ModePerm, 0640)\n\t}\n}\n"
  },
  {
    "path": "internal/robustio/UPSTREAM",
    "content": "This package is a copy of cmd/go/internal/robustio.\nIt is mostly in sync with upstream according to the last commit we've looked at,\nwith the exception of still using I/O functions that work with older Go versions.\n\nThe last upstream commit we've looked at was:\ndc04f3ba1f25313bc9c97e728620206c235db9ee\n"
  },
  {
    "path": "internal/robustio/robustio.go",
    "content": "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package robustio wraps I/O functions that are prone to failure on Windows,\n// transparently retrying errors up to an arbitrary timeout.\n//\n// Errors are classified heuristically and retries are bounded, so the functions\n// in this package do not completely eliminate spurious errors. However, they do\n// significantly reduce the rate of failure in practice.\n//\n// If so, the error will likely wrap one of:\n// The functions in this package do not completely eliminate spurious errors,\n// but substantially reduce their rate of occurrence in practice.\npackage robustio\n\n// Rename is like os.Rename, but on Windows retries errors that may occur if the\n// file is concurrently read or overwritten.\n//\n// (See golang.org/issue/31247 and golang.org/issue/32188.)\nfunc Rename(oldpath, newpath string) error {\n\treturn rename(oldpath, newpath)\n}\n\n// ReadFile is like os.ReadFile, but on Windows retries errors that may\n// occur if the file is concurrently replaced.\n//\n// (See golang.org/issue/31247 and golang.org/issue/32188.)\nfunc ReadFile(filename string) ([]byte, error) {\n\treturn readFile(filename)\n}\n\n// RemoveAll is like os.RemoveAll, but on Windows retries errors that may occur\n// if an executable file in the directory has recently been executed.\n//\n// (See golang.org/issue/19491.)\nfunc RemoveAll(path string) error {\n\treturn removeAll(path)\n}\n\n// IsEphemeralError reports whether err is one of the errors that the functions\n// in this package attempt to mitigate.\n//\n// Errors considered ephemeral include:\n//   - syscall.ERROR_ACCESS_DENIED\n//   - syscall.ERROR_FILE_NOT_FOUND\n//   - internal/syscall/windows.ERROR_SHARING_VIOLATION\n//\n// This set may be expanded in the future; programs must not rely on the\n// non-ephemerality of any given error.\nfunc IsEphemeralError(err error) bool {\n\treturn isEphemeralError(err)\n}\n"
  },
  {
    "path": "internal/robustio/robustio_darwin.go",
    "content": "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage robustio\n\nimport (\n\t\"errors\"\n\t\"syscall\"\n)\n\nconst errFileNotFound = syscall.ENOENT\n\n// isEphemeralError returns true if err may be resolved by waiting.\nfunc isEphemeralError(err error) bool {\n\tvar errno syscall.Errno\n\tif errors.As(err, &errno) {\n\t\treturn errno == errFileNotFound\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "internal/robustio/robustio_flaky.go",
    "content": "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n//go:build windows || darwin\n\npackage robustio\n\nimport (\n\t\"errors\"\n\t\"math/rand\"\n\t\"os\"\n\t\"syscall\"\n\t\"time\"\n)\n\nconst arbitraryTimeout = 2000 * time.Millisecond\n\n// retry retries ephemeral errors from f up to an arbitrary timeout\n// to work around filesystem flakiness on Windows and Darwin.\nfunc retry(f func() (err error, mayRetry bool)) error {\n\tvar (\n\t\tbestErr     error\n\t\tlowestErrno syscall.Errno\n\t\tstart       time.Time\n\t\tnextSleep   time.Duration = 1 * time.Millisecond\n\t)\n\tfor {\n\t\terr, mayRetry := f()\n\t\tif err == nil || !mayRetry {\n\t\t\treturn err\n\t\t}\n\n\t\tvar errno syscall.Errno\n\t\tif errors.As(err, &errno) && (lowestErrno == 0 || errno < lowestErrno) {\n\t\t\tbestErr = err\n\t\t\tlowestErrno = errno\n\t\t} else if bestErr == nil {\n\t\t\tbestErr = err\n\t\t}\n\n\t\tif start.IsZero() {\n\t\t\tstart = time.Now()\n\t\t} else if d := time.Since(start) + nextSleep; d >= arbitraryTimeout {\n\t\t\tbreak\n\t\t}\n\t\ttime.Sleep(nextSleep)\n\t\tnextSleep += time.Duration(rand.Int63n(int64(nextSleep)))\n\t}\n\n\treturn bestErr\n}\n\n// rename is like os.Rename, but retries ephemeral errors.\n//\n// On Windows it wraps os.Rename, which (as of 2019-06-04) uses MoveFileEx with\n// MOVEFILE_REPLACE_EXISTING.\n//\n// Windows also provides a different system call, ReplaceFile,\n// that provides similar semantics, but perhaps preserves more metadata. (The\n// documentation on the differences between the two is very sparse.)\n//\n// Empirical error rates with MoveFileEx are lower under modest concurrency, so\n// for now we're sticking with what the os package already provides.\nfunc rename(oldpath, newpath string) (err error) {\n\treturn retry(func() (err error, mayRetry bool) {\n\t\terr = os.Rename(oldpath, newpath)\n\t\treturn err, isEphemeralError(err)\n\t})\n}\n\n// readFile is like os.ReadFile, but retries ephemeral errors.\nfunc readFile(filename string) ([]byte, error) {\n\tvar b []byte\n\terr := retry(func() (err error, mayRetry bool) {\n\t\tb, err = os.ReadFile(filename)\n\n\t\t// Unlike in rename, we do not retry errFileNotFound here: it can occur\n\t\t// as a spurious error, but the file may also genuinely not exist, so the\n\t\t// increase in robustness is probably not worth the extra latency.\n\t\treturn err, isEphemeralError(err) && !errors.Is(err, errFileNotFound)\n\t})\n\treturn b, err\n}\n\nfunc removeAll(path string) error {\n\treturn retry(func() (err error, mayRetry bool) {\n\t\terr = os.RemoveAll(path)\n\t\treturn err, isEphemeralError(err)\n\t})\n}\n"
  },
  {
    "path": "internal/robustio/robustio_other.go",
    "content": "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n//go:build !windows && !darwin\n\npackage robustio\n\nimport (\n\t\"os\"\n)\n\nfunc rename(oldpath, newpath string) error {\n\treturn os.Rename(oldpath, newpath)\n}\n\nfunc readFile(filename string) ([]byte, error) {\n\treturn os.ReadFile(filename)\n}\n\nfunc removeAll(path string) error {\n\treturn os.RemoveAll(path)\n}\n\nfunc isEphemeralError(err error) bool {\n\treturn false\n}\n"
  },
  {
    "path": "internal/robustio/robustio_windows.go",
    "content": "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage robustio\n\nimport (\n\t\"errors\"\n\t\"syscall\"\n)\n\nconst ERROR_SHARING_VIOLATION = 32\nconst errFileNotFound = syscall.ERROR_FILE_NOT_FOUND\n\n// isEphemeralError returns true if err may be resolved by waiting.\nfunc isEphemeralError(err error) bool {\n\tvar errno syscall.Errno\n\tif errors.As(err, &errno) {\n\t\tswitch errno {\n\t\tcase syscall.ERROR_ACCESS_DENIED,\n\t\t\tsyscall.ERROR_FILE_NOT_FOUND,\n\t\t\tERROR_SHARING_VIOLATION:\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "internal/sharedcheck/lint.go",
    "content": "package sharedcheck\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/facts/tokenfile\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nfunc CheckRangeStringRunes(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tcb := func(node ast.Node) bool {\n\t\t\trng, ok := node.(*ast.RangeStmt)\n\t\t\tif !ok || !astutil.IsBlank(rng.Key) {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tv, _ := fn.ValueForExpr(rng.X)\n\n\t\t\t// Check that we're converting from string to []rune\n\t\t\tval, _ := v.(*ir.Convert)\n\t\t\tif val == nil {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tTsrc, ok := typeutil.CoreType(val.X.Type()).(*types.Basic)\n\t\t\tif !ok || Tsrc.Kind() != types.String {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tTdst, ok := typeutil.CoreType(val.Type()).(*types.Slice)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tTdstElem, ok := types.Unalias(Tdst.Elem()).(*types.Basic)\n\t\t\tif !ok || TdstElem.Kind() != types.Int32 {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\t// Check that the result of the conversion is only used to\n\t\t\t// range over\n\t\t\trefs := val.Referrers()\n\t\t\tif refs == nil {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\t// Expect two refs: one for obtaining the length of the slice,\n\t\t\t// one for accessing the elements\n\t\t\tif len(irutil.FilterDebug(*refs)) != 2 {\n\t\t\t\t// TODO(dh): right now, we check that only one place\n\t\t\t\t// refers to our slice. This will miss cases such as\n\t\t\t\t// ranging over the slice twice. Ideally, we'd ensure that\n\t\t\t\t// the slice is only used for ranging over (without\n\t\t\t\t// accessing the key), but that is harder to do because in\n\t\t\t\t// IR form, ranging over a slice looks like an ordinary\n\t\t\t\t// loop with index increments and slice accesses. We'd\n\t\t\t\t// have to look at the associated AST node to check that\n\t\t\t\t// it's a range statement.\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tpass.Reportf(rng.Pos(), \"should range over string, not []rune(string)\")\n\n\t\t\treturn true\n\t\t}\n\t\tif source := fn.Source(); source != nil {\n\t\t\tast.Inspect(source, cb)\n\t\t}\n\t}\n\treturn nil, nil\n}\n\n// RedundantTypeInDeclarationChecker returns a checker that flags variable declarations with redundantly specified types.\n// That is, it flags 'var v T = e' where e's type is identical to T and 'var v = e' (or 'v := e') would have the same effect.\n//\n// It does not flag variables under the following conditions, to reduce the number of false positives:\n// - global variables – these often specify types to aid godoc\n// - files that use cgo – cgo code generation and pointer checking emits redundant types\n//\n// It does not flag variables under the following conditions, unless flagHelpfulTypes is true, to reduce the number of noisy positives:\n// - packages that import syscall or unsafe – these sometimes use this form of assignment to make sure types are as expected\n// - variables named the blank identifier – a pattern used to confirm the types of variables\n// - untyped expressions on the rhs – the explicitness might aid readability\nfunc RedundantTypeInDeclarationChecker(verb string, flagHelpfulTypes bool) *analysis.Analyzer {\n\tfn := func(pass *analysis.Pass) (any, error) {\n\t\teval := func(expr ast.Expr) (types.TypeAndValue, error) {\n\t\t\tinfo := &types.Info{\n\t\t\t\tTypes: map[ast.Expr]types.TypeAndValue{},\n\t\t\t}\n\t\t\terr := types.CheckExpr(pass.Fset, pass.Pkg, expr.Pos(), expr, info)\n\t\t\treturn info.Types[expr], err\n\t\t}\n\n\t\tif !flagHelpfulTypes {\n\t\t\t// Don't look at code in low-level packages\n\t\t\tfor _, imp := range pass.Pkg.Imports() {\n\t\t\t\tif imp.Path() == \"syscall\" || imp.Path() == \"unsafe\" {\n\t\t\t\t\treturn nil, nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfn := func(node ast.Node) {\n\t\t\tdecl := node.(*ast.GenDecl)\n\t\t\tif decl.Tok != token.VAR {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tgen, _ := code.Generator(pass, decl.Pos())\n\t\t\tif gen == generated.Cgo {\n\t\t\t\t// TODO(dh): remove this exception once we can use UsesCgo\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Delay looking up parent AST nodes until we have to\n\t\t\tcheckedDecl := false\n\n\t\tspecLoop:\n\t\t\tfor _, spec := range decl.Specs {\n\t\t\t\tspec := spec.(*ast.ValueSpec)\n\t\t\t\tif spec.Type == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif len(spec.Names) != len(spec.Values) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tTlhs := pass.TypesInfo.TypeOf(spec.Type)\n\t\t\t\tfor i, v := range spec.Values {\n\t\t\t\t\tif !flagHelpfulTypes && spec.Names[i].Name == \"_\" {\n\t\t\t\t\t\tcontinue specLoop\n\t\t\t\t\t}\n\t\t\t\t\tTrhs := pass.TypesInfo.TypeOf(v)\n\t\t\t\t\tif !types.Identical(Tlhs, Trhs) {\n\t\t\t\t\t\tcontinue specLoop\n\t\t\t\t\t}\n\n\t\t\t\t\t// Some expressions are untyped and get converted to the lhs type implicitly.\n\t\t\t\t\t// This applies to untyped constants, shift operations with an untyped lhs, and possibly others.\n\t\t\t\t\t//\n\t\t\t\t\t// Check if the type is truly redundant, i.e. if the type on the lhs doesn't match the default type of the untyped constant.\n\t\t\t\t\ttv, err := eval(v)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tpanic(err)\n\t\t\t\t\t}\n\t\t\t\t\tif b, ok := types.Unalias(tv.Type).(*types.Basic); ok && (b.Info()&types.IsUntyped) != 0 {\n\t\t\t\t\t\tif Tlhs != types.Default(b) {\n\t\t\t\t\t\t\t// The rhs is untyped and its default type differs from the explicit type on the lhs\n\t\t\t\t\t\t\tcontinue specLoop\n\t\t\t\t\t\t}\n\t\t\t\t\t\tswitch v := v.(type) {\n\t\t\t\t\t\tcase *ast.Ident:\n\t\t\t\t\t\t\t// Only flag named constant rhs if it's a predeclared identifier.\n\t\t\t\t\t\t\t// Don't flag other named constants, as the explicit type may aid readability.\n\t\t\t\t\t\t\tif pass.TypesInfo.ObjectOf(v).Pkg() != nil && !flagHelpfulTypes {\n\t\t\t\t\t\t\t\tcontinue specLoop\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\tcase *ast.BasicLit:\n\t\t\t\t\t\t\t// Do flag basic literals\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t// Don't flag untyped rhs expressions unless flagHelpfulTypes is set\n\t\t\t\t\t\t\tif !flagHelpfulTypes {\n\t\t\t\t\t\t\t\tcontinue specLoop\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif !checkedDecl {\n\t\t\t\t\t// Don't flag global variables. These often have explicit types for godoc's sake.\n\t\t\t\t\tpath, _ := astutil.PathEnclosingInterval(code.File(pass, decl), decl.Pos(), decl.Pos())\n\t\t\t\tpathLoop:\n\t\t\t\t\tfor _, el := range path {\n\t\t\t\t\t\tswitch el.(type) {\n\t\t\t\t\t\tcase *ast.FuncDecl, *ast.FuncLit:\n\t\t\t\t\t\t\tcheckedDecl = true\n\t\t\t\t\t\t\tbreak pathLoop\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif !checkedDecl {\n\t\t\t\t\t\t// decl is not inside a function\n\t\t\t\t\t\tbreak specLoop\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treport.Report(pass, spec.Type, fmt.Sprintf(\"%s omit type %s from declaration; it will be inferred from the right-hand side\", verb, report.Render(pass, spec.Type)), report.FilterGenerated(),\n\t\t\t\t\treport.Fixes(edit.Fix(\"Remove redundant type\", edit.Delete(spec.Type))))\n\t\t\t}\n\t\t}\n\t\tcode.Preorder(pass, fn, (*ast.GenDecl)(nil))\n\t\treturn nil, nil\n\t}\n\n\treturn &analysis.Analyzer{\n\t\tRun:      fn,\n\t\tRequires: []*analysis.Analyzer{generated.Analyzer, inspect.Analyzer, tokenfile.Analyzer},\n\t}\n}\n"
  },
  {
    "path": "internal/sync/sync.go",
    "content": "package sync\n\ntype Semaphore struct {\n\tch chan struct{}\n}\n\nfunc NewSemaphore(size int) Semaphore {\n\treturn Semaphore{\n\t\tch: make(chan struct{}, size),\n\t}\n}\n\nfunc (sem Semaphore) Acquire() {\n\tsem.ch <- struct{}{}\n}\n\nfunc (sem Semaphore) AcquireMaybe() bool {\n\tselect {\n\tcase sem.ch <- struct{}{}:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (sem Semaphore) Release() {\n\t<-sem.ch\n}\n\nfunc (sem Semaphore) Len() int {\n\treturn len(sem.ch)\n}\n\nfunc (sem Semaphore) Cap() int {\n\treturn cap(sem.ch)\n}\n"
  },
  {
    "path": "internal/testenv/UPSTREAM",
    "content": "This package is a copy of golang.org/x/tools/internal/testenv,\nimported from commit 33e937220d8f91f1d242ad15aebc3e245aca5515.\n\nThe last upstream commit we've looked at was: 414ec9c3f0ea84250efb8f18f51c607184b7631e\n"
  },
  {
    "path": "internal/testenv/testenv.go",
    "content": "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package testenv contains helper functions for skipping tests\n// based on which tools are present in the environment.\npackage testenv\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"go/build\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\texec \"golang.org/x/sys/execabs\"\n)\n\n// Testing is an abstraction of a *testing.T.\ntype Testing interface {\n\tSkipf(format string, args ...any)\n\tFatalf(format string, args ...any)\n}\n\ntype helperer interface {\n\tHelper()\n}\n\n// packageMainIsDevel reports whether the module containing package main\n// is a development version (if module information is available).\n//\n// Builds in GOPATH mode and builds that lack module information are assumed to\n// be development versions.\nvar packageMainIsDevel = func() bool { return true }\n\nvar checkGoGoroot struct {\n\tonce sync.Once\n\terr  error\n}\n\nfunc hasTool(tool string) error {\n\tif tool == \"cgo\" {\n\t\tenabled, err := cgoEnabled(false)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"checking cgo: %v\", err)\n\t\t}\n\t\tif !enabled {\n\t\t\treturn fmt.Errorf(\"cgo not enabled\")\n\t\t}\n\t\treturn nil\n\t}\n\n\t_, err := exec.LookPath(tool)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch tool {\n\tcase \"patch\":\n\t\t// check that the patch tools supports the -o argument\n\t\ttemp, err := os.CreateTemp(\"\", \"patch-test\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttemp.Close()\n\t\tdefer os.Remove(temp.Name())\n\t\tcmd := exec.Command(tool, \"-o\", temp.Name())\n\t\tif err := cmd.Run(); err != nil {\n\t\t\treturn err\n\t\t}\n\n\tcase \"go\":\n\t\tcheckGoGoroot.once.Do(func() {\n\t\t\t// Ensure that the 'go' command found by exec.LookPath is from the correct\n\t\t\t// GOROOT. Otherwise, 'some/path/go test ./...' will test against some\n\t\t\t// version of the 'go' binary other than 'some/path/go', which is almost\n\t\t\t// certainly not what the user intended.\n\t\t\tout, err := exec.Command(tool, \"env\", \"GOROOT\").CombinedOutput()\n\t\t\tif err != nil {\n\t\t\t\tcheckGoGoroot.err = err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tGOROOT := strings.TrimSpace(string(out))\n\t\t\t//lint:ignore SA1019 runtime.GOROOT is deprecated, specifically to allow copying binaries. What should we do\n\t\t\t//  about this?\n\t\t\tif runtimeGOROOT := runtime.GOROOT(); GOROOT != runtimeGOROOT {\n\t\t\t\tcheckGoGoroot.err = fmt.Errorf(\"'go env GOROOT' does not match runtime.GOROOT:\\n\\tgo env: %s\\n\\tGOROOT: %s\", GOROOT, runtimeGOROOT)\n\t\t\t}\n\t\t})\n\t\tif checkGoGoroot.err != nil {\n\t\t\treturn checkGoGoroot.err\n\t\t}\n\n\tcase \"diff\":\n\t\t// Check that diff is the GNU version, needed for the -u argument and\n\t\t// to report missing newlines at the end of files.\n\t\tout, err := exec.Command(tool, \"-version\").Output()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif !bytes.Contains(out, []byte(\"GNU diffutils\")) {\n\t\t\treturn fmt.Errorf(\"diff is not the GNU version\")\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc cgoEnabled(bypassEnvironment bool) (bool, error) {\n\tcmd := exec.Command(\"go\", \"env\", \"CGO_ENABLED\")\n\tif bypassEnvironment {\n\t\tcmd.Env = append(os.Environ(), \"CGO_ENABLED=\")\n\t}\n\tout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tenabled := strings.TrimSpace(string(out))\n\treturn enabled == \"1\", nil\n}\n\nfunc allowMissingTool(tool string) bool {\n\tif runtime.GOOS == \"android\" {\n\t\t// Android builds generally run tests on a separate machine from the build,\n\t\t// so don't expect any external tools to be available.\n\t\treturn true\n\t}\n\n\tswitch tool {\n\tcase \"cgo\":\n\t\tif strings.HasSuffix(os.Getenv(\"GO_BUILDER_NAME\"), \"-nocgo\") {\n\t\t\t// Explicitly disabled on -nocgo builders.\n\t\t\treturn true\n\t\t}\n\t\tif enabled, err := cgoEnabled(true); err == nil && !enabled {\n\t\t\t// No platform support.\n\t\t\treturn true\n\t\t}\n\tcase \"go\":\n\t\tif os.Getenv(\"GO_BUILDER_NAME\") == \"illumos-amd64-joyent\" {\n\t\t\t// Work around a misconfigured builder (see https://golang.org/issue/33950).\n\t\t\treturn true\n\t\t}\n\tcase \"diff\":\n\t\tif os.Getenv(\"GO_BUILDER_NAME\") != \"\" {\n\t\t\treturn true\n\t\t}\n\tcase \"patch\":\n\t\tif os.Getenv(\"GO_BUILDER_NAME\") != \"\" {\n\t\t\treturn true\n\t\t}\n\t}\n\n\t// If a developer is actively working on this test, we expect them to have all\n\t// of its dependencies installed. However, if it's just a dependency of some\n\t// other module (for example, being run via 'go test all'), we should be more\n\t// tolerant of unusual environments.\n\treturn !packageMainIsDevel()\n}\n\n// NeedsTool skips t if the named tool is not present in the path.\n// As a special case, \"cgo\" means \"go\" is present and can compile cgo programs.\nfunc NeedsTool(t Testing, tool string) {\n\tif t, ok := t.(helperer); ok {\n\t\tt.Helper()\n\t}\n\terr := hasTool(tool)\n\tif err == nil {\n\t\treturn\n\t}\n\tif allowMissingTool(tool) {\n\t\tt.Skipf(\"skipping because %s tool not available: %v\", tool, err)\n\t} else {\n\t\tt.Fatalf(\"%s tool not available: %v\", tool, err)\n\t}\n}\n\n// NeedsGoPackages skips t if the go/packages driver (or 'go' tool) implied by\n// the current process environment is not present in the path.\nfunc NeedsGoPackages(t Testing) {\n\tif t, ok := t.(helperer); ok {\n\t\tt.Helper()\n\t}\n\n\ttool := os.Getenv(\"GOPACKAGESDRIVER\")\n\tswitch tool {\n\tcase \"off\":\n\t\t// \"off\" forces go/packages to use the go command.\n\t\ttool = \"go\"\n\tcase \"\":\n\t\tif _, err := exec.LookPath(\"gopackagesdriver\"); err == nil {\n\t\t\ttool = \"gopackagesdriver\"\n\t\t} else {\n\t\t\ttool = \"go\"\n\t\t}\n\t}\n\n\tNeedsTool(t, tool)\n}\n\n// NeedsGoPackagesEnv skips t if the go/packages driver (or 'go' tool) implied\n// by env is not present in the path.\nfunc NeedsGoPackagesEnv(t Testing, env []string) {\n\tif t, ok := t.(helperer); ok {\n\t\tt.Helper()\n\t}\n\n\tfor _, v := range env {\n\t\tif after, ok := strings.CutPrefix(v, \"GOPACKAGESDRIVER=\"); ok {\n\t\t\ttool := after\n\t\t\tif tool == \"off\" {\n\t\t\t\tNeedsTool(t, \"go\")\n\t\t\t} else {\n\t\t\t\tNeedsTool(t, tool)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\n\tNeedsGoPackages(t)\n}\n\n// NeedsGoBuild skips t if the current system can't build programs with “go build”\n// and then run them with os.StartProcess or exec.Command.\n// android, and darwin/arm systems don't have the userspace go build needs to run,\n// and js/wasm doesn't support running subprocesses.\nfunc NeedsGoBuild(t Testing) {\n\tif t, ok := t.(helperer); ok {\n\t\tt.Helper()\n\t}\n\n\tNeedsTool(t, \"go\")\n\n\tswitch runtime.GOOS {\n\tcase \"android\", \"js\":\n\t\tt.Skipf(\"skipping test: %v can't build and run Go binaries\", runtime.GOOS)\n\tcase \"darwin\":\n\t\tif strings.HasPrefix(runtime.GOARCH, \"arm\") {\n\t\t\tt.Skipf(\"skipping test: darwin/arm can't build and run Go binaries\")\n\t\t}\n\t}\n}\n\n// ExitIfSmallMachine emits a helpful diagnostic and calls os.Exit(0) if the\n// current machine is a builder known to have scarce resources.\n//\n// It should be called from within a TestMain function.\nfunc ExitIfSmallMachine() {\n\tswitch b := os.Getenv(\"GO_BUILDER_NAME\"); b {\n\tcase \"linux-arm-scaleway\":\n\t\t// \"linux-arm\" was renamed to \"linux-arm-scaleway\" in CL 303230.\n\t\tfmt.Fprintln(os.Stderr, \"skipping test: linux-arm-scaleway builder lacks sufficient memory (https://golang.org/issue/32834)\")\n\tcase \"plan9-arm\":\n\t\tfmt.Fprintln(os.Stderr, \"skipping test: plan9-arm builder lacks sufficient memory (https://golang.org/issue/38772)\")\n\tcase \"netbsd-arm-bsiegert\", \"netbsd-arm64-bsiegert\":\n\t\t// As of 2021-06-02, these builders are running with GO_TEST_TIMEOUT_SCALE=10,\n\t\t// and there is only one of each. We shouldn't waste those scarce resources\n\t\t// running very slow tests.\n\t\tfmt.Fprintf(os.Stderr, \"skipping test: %s builder is very slow\\n\", b)\n\tcase \"dragonfly-amd64\":\n\t\t// As of 2021-11-02, this builder is running with GO_TEST_TIMEOUT_SCALE=2,\n\t\t// and seems to have unusually slow disk performance.\n\t\tfmt.Fprintln(os.Stderr, \"skipping test: dragonfly-amd64 has slow disk (https://golang.org/issue/45216)\")\n\tcase \"linux-riscv64-unmatched\":\n\t\t// As of 2021-11-03, this builder is empirically not fast enough to run\n\t\t// gopls tests. Ideally we should make the tests faster in short mode\n\t\t// and/or fix them to not assume arbitrary deadlines.\n\t\t// For now, we'll skip them instead.\n\t\tfmt.Fprintf(os.Stderr, \"skipping test: %s builder is too slow (https://golang.org/issue/49321)\\n\", b)\n\tdefault:\n\t\treturn\n\t}\n\tos.Exit(0)\n}\n\n// Go1Point returns the x in Go 1.x.\nfunc Go1Point() int {\n\tfor i := len(build.Default.ReleaseTags) - 1; i >= 0; i-- {\n\t\tvar version int\n\t\tif _, err := fmt.Sscanf(build.Default.ReleaseTags[i], \"go1.%d\", &version); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\treturn version\n\t}\n\tpanic(\"bad release tags\")\n}\n\n// NeedsGo1Point skips t if the Go version used to run the test is older than\n// 1.x.\nfunc NeedsGo1Point(t Testing, x int) {\n\tif t, ok := t.(helperer); ok {\n\t\tt.Helper()\n\t}\n\tif Go1Point() < x {\n\t\tt.Skipf(\"running Go version %q is version 1.%d, older than required 1.%d\", runtime.Version(), Go1Point(), x)\n\t}\n}\n\n// SkipAfterGo1Point skips t if the Go version used to run the test is newer than\n// 1.x.\nfunc SkipAfterGo1Point(t Testing, x int) {\n\tif t, ok := t.(helperer); ok {\n\t\tt.Helper()\n\t}\n\tif Go1Point() > x {\n\t\tt.Skipf(\"running Go version %q is version 1.%d, newer than maximum 1.%d\", runtime.Version(), Go1Point(), x)\n\t}\n}\n\n// Deadline returns the deadline of t, if known,\n// using the Deadline method added in Go 1.15.\nfunc Deadline(t Testing) (time.Time, bool) {\n\ttd, ok := t.(interface {\n\t\tDeadline() (time.Time, bool)\n\t})\n\tif !ok {\n\t\treturn time.Time{}, false\n\t}\n\treturn td.Deadline()\n}\n"
  },
  {
    "path": "internal/testenv/testenv_112.go",
    "content": "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n//go:build go1.12\n// +build go1.12\n\npackage testenv\n\nimport \"runtime/debug\"\n\nfunc packageMainIsDevelModule() bool {\n\tinfo, ok := debug.ReadBuildInfo()\n\tif !ok {\n\t\t// Most test binaries currently lack build info, but this should become more\n\t\t// permissive once https://golang.org/issue/33976 is fixed.\n\t\treturn true\n\t}\n\n\t// Note: info.Main.Version describes the version of the module containing\n\t// package main, not the version of “the main module”.\n\t// See https://golang.org/issue/33975.\n\treturn info.Main.Version == \"(devel)\"\n}\n\nfunc init() {\n\tpackageMainIsDevel = packageMainIsDevelModule\n}\n"
  },
  {
    "path": "internal/typesinternal/typeindex/typeindex.go",
    "content": "// Copyright 2025 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package typeindex provides an [Index] of type information for a\n// package, allowing efficient lookup of, say, whether a given symbol\n// is referenced and, if so, where from; or of the [inspector.Cursor] for\n// the declaration of a particular [types.Object] symbol.\npackage typeindex\n\nimport (\n\t\"encoding/binary\"\n\t\"go/ast\"\n\t\"go/types\"\n\t\"iter\"\n\n\t\"golang.org/x/tools/go/ast/edge\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n\t\"golang.org/x/tools/go/types/typeutil\"\n)\n\n// IsPackageLevel reports whether obj is a package-level symbol.\nfunc IsPackageLevel(obj types.Object) bool {\n\treturn obj.Pkg() != nil && obj.Parent() == obj.Pkg().Scope()\n}\n\n// New constructs an Index for the package of type-annotated syntax\n//\n// TODO(adonovan): accept a FileSet too?\n// We regret not requiring one in inspector.New.\nfunc New(inspect *inspector.Inspector, pkg *types.Package, info *types.Info) *Index {\n\tix := &Index{\n\t\tinspect:  inspect,\n\t\tinfo:     info,\n\t\tpackages: make(map[string]*types.Package),\n\t\tdef:      make(map[types.Object]inspector.Cursor),\n\t\tuses:     make(map[types.Object]*uses),\n\t}\n\n\taddPackage := func(pkg2 *types.Package) {\n\t\tif pkg2 != nil && pkg2 != pkg {\n\t\t\tix.packages[pkg2.Path()] = pkg2\n\t\t}\n\t}\n\n\tfor cur := range inspect.Root().Preorder((*ast.ImportSpec)(nil), (*ast.Ident)(nil)) {\n\t\tswitch n := cur.Node().(type) {\n\t\tcase *ast.ImportSpec:\n\t\t\t// Index direct imports, including blank ones.\n\t\t\tif pkgname := info.PkgNameOf(n); pkgname != nil {\n\t\t\t\taddPackage(pkgname.Imported())\n\t\t\t}\n\n\t\tcase *ast.Ident:\n\t\t\t// Index all defining and using identifiers.\n\t\t\tif obj := info.Defs[n]; obj != nil {\n\t\t\t\tix.def[obj] = cur\n\t\t\t}\n\n\t\t\tif obj := info.Uses[n]; obj != nil {\n\t\t\t\t// Index indirect dependencies (via fields and methods).\n\t\t\t\tif !IsPackageLevel(obj) {\n\t\t\t\t\taddPackage(obj.Pkg())\n\t\t\t\t}\n\n\t\t\t\tus, ok := ix.uses[obj]\n\t\t\t\tif !ok {\n\t\t\t\t\tus = &uses{}\n\t\t\t\t\tus.code = us.initial[:0]\n\t\t\t\t\tix.uses[obj] = us\n\t\t\t\t}\n\t\t\t\tdelta := cur.Index() - us.last\n\t\t\t\tif delta < 0 {\n\t\t\t\t\tpanic(\"non-monotonic\")\n\t\t\t\t}\n\t\t\t\tus.code = binary.AppendUvarint(us.code, uint64(delta))\n\t\t\t\tus.last = cur.Index()\n\t\t\t}\n\t\t}\n\t}\n\treturn ix\n}\n\n// An Index holds an index mapping [types.Object] symbols to their syntax.\n// In effect, it is the inverse of [types.Info].\ntype Index struct {\n\tinspect  *inspector.Inspector\n\tinfo     *types.Info\n\tpackages map[string]*types.Package         // packages of all symbols referenced from this package\n\tdef      map[types.Object]inspector.Cursor // Cursor of *ast.Ident that defines the Object\n\tuses     map[types.Object]*uses            // Cursors of *ast.Idents that use the Object\n}\n\n// A uses holds the list of Cursors of Idents that use a given symbol.\n//\n// The Uses map of [types.Info] is substantial, so it pays to compress\n// its inverse mapping here, both in space and in CPU due to reduced\n// allocation. A Cursor is 2 words; a Cursor.Index is 4 bytes; but\n// since Cursors are naturally delivered in ascending order, we can\n// use varint-encoded deltas at a cost of only ~1.7-2.2 bytes per use.\n//\n// Many variables have only one or two uses, so their encoded uses may\n// fit in the 4 bytes of initial, saving further CPU and space\n// essentially for free since the struct's size class is 4 words.\ntype uses struct {\n\tcode    []byte  // varint-encoded deltas of successive Cursor.Index values\n\tlast    int32   // most recent Cursor.Index value; used during encoding\n\tinitial [4]byte // use slack in size class as initial space for code\n}\n\n// Uses returns the sequence of Cursors of [*ast.Ident]s in this package\n// that refer to obj. If obj is nil, the sequence is empty.\nfunc (ix *Index) Uses(obj types.Object) iter.Seq[inspector.Cursor] {\n\treturn func(yield func(inspector.Cursor) bool) {\n\t\tif uses := ix.uses[obj]; uses != nil {\n\t\t\tvar last int32\n\t\t\tfor code := uses.code; len(code) > 0; {\n\t\t\t\tdelta, n := binary.Uvarint(code)\n\t\t\t\tlast += int32(delta)\n\t\t\t\tif !yield(ix.inspect.At(last)) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tcode = code[n:]\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Used reports whether any of the specified objects are used, in\n// other words, obj != nil && Uses(obj) is non-empty for some obj in objs.\n//\n// (This treatment of nil allows Used to be called directly on the\n// result of [Index.Object] so that analyzers can conveniently skip\n// packages that don't use a symbol of interest.)\nfunc (ix *Index) Used(objs ...types.Object) bool {\n\tfor _, obj := range objs {\n\t\tif obj != nil && ix.uses[obj] != nil {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Def returns the Cursor of the [*ast.Ident] in this package\n// that declares the specified object, if any.\nfunc (ix *Index) Def(obj types.Object) (inspector.Cursor, bool) {\n\tcur, ok := ix.def[obj]\n\treturn cur, ok\n}\n\n// Package returns the package of the specified path,\n// or nil if it is not referenced from this package.\nfunc (ix *Index) Package(path string) *types.Package {\n\treturn ix.packages[path]\n}\n\n// Object returns the package-level symbol name within the package of\n// the specified path, or nil if the package or symbol does not exist\n// or is not visible from this package.\nfunc (ix *Index) Object(path, name string) types.Object {\n\tif pkg := ix.Package(path); pkg != nil {\n\t\treturn pkg.Scope().Lookup(name)\n\t}\n\treturn nil\n}\n\n// Selection returns the named method or field belonging to the\n// package-level type returned by Object(path, typename).\nfunc (ix *Index) Selection(path, typename, name string) types.Object {\n\tif obj := ix.Object(path, typename); obj != nil {\n\t\tif tname, ok := obj.(*types.TypeName); ok {\n\t\t\tobj, _, _ := types.LookupFieldOrMethod(tname.Type(), true, obj.Pkg(), name)\n\t\t\treturn obj\n\t\t}\n\t}\n\treturn nil\n}\n\n// Calls returns the sequence of cursors for *ast.CallExpr nodes that\n// call the specified callee, as defined by [typeutil.Callee].\n// If callee is nil, the sequence is empty.\nfunc (ix *Index) Calls(callee types.Object) iter.Seq[inspector.Cursor] {\n\treturn func(yield func(inspector.Cursor) bool) {\n\t\tfor cur := range ix.Uses(callee) {\n\t\t\tek, _ := cur.ParentEdge()\n\n\t\t\t// The call may be of the form f() or x.f(),\n\t\t\t// optionally with parens; ascend from f to call.\n\t\t\t//\n\t\t\t// It is tempting but wrong to use the first\n\t\t\t// CallExpr ancestor: we have to make sure the\n\t\t\t// ident is in the CallExpr.Fun position, otherwise\n\t\t\t// f(f, f) would have two spurious matches.\n\t\t\t// Avoiding Enclosing is also significantly faster.\n\n\t\t\t// inverse unparen: f -> (f)\n\t\t\tfor ek == edge.ParenExpr_X {\n\t\t\t\tcur = cur.Parent()\n\t\t\t\tek, _ = cur.ParentEdge()\n\t\t\t}\n\n\t\t\t// ascend selector: f -> x.f\n\t\t\tif ek == edge.SelectorExpr_Sel {\n\t\t\t\tcur = cur.Parent()\n\t\t\t\tek, _ = cur.ParentEdge()\n\t\t\t}\n\n\t\t\t// inverse unparen again\n\t\t\tfor ek == edge.ParenExpr_X {\n\t\t\t\tcur = cur.Parent()\n\t\t\t\tek, _ = cur.ParentEdge()\n\t\t\t}\n\n\t\t\t// ascend from f or x.f to call\n\t\t\tif ek == edge.CallExpr_Fun {\n\t\t\t\tcurCall := cur.Parent()\n\t\t\t\tcall := curCall.Node().(*ast.CallExpr)\n\t\t\t\tif typeutil.Callee(ix.info, call) == callee {\n\t\t\t\t\tif !yield(curCall) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "knowledge/arg.go",
    "content": "package knowledge\n\nvar Args = map[string]int{\n\t\"(*sync.Pool).Put.x\":                     0,\n\t\"(*text/template.Template).Parse.text\":   0,\n\t\"(io.Seeker).Seek.offset\":                0,\n\t\"(time.Time).Sub.u\":                      0,\n\t\"append.elems\":                           1,\n\t\"append.slice\":                           0,\n\t\"bytes.Equal.a\":                          0,\n\t\"bytes.Equal.b\":                          1,\n\t\"encoding/ascii85.Encode.dst\":            0,\n\t\"encoding/ascii85.Encode.src\":            1,\n\t\"(*encoding/base32.Encoding).Encode.dst\": 0,\n\t\"(*encoding/base32.Encoding).Encode.src\": 1,\n\t\"(*encoding/base64.Encoding).Encode.dst\": 0,\n\t\"(*encoding/base64.Encoding).Encode.src\": 1,\n\t\"encoding/binary.Write.data\":             2,\n\t\"encoding/hex.Encode.dst\":                0,\n\t\"encoding/hex.Encode.src\":                1,\n\t\"(*encoding/json.Decoder).Decode.v\":      0,\n\t\"(*encoding/json.Encoder).Encode.v\":      0,\n\t\"(*encoding/xml.Decoder).Decode.v\":       0,\n\t\"(*encoding/xml.Encoder).Encode.v\":       0,\n\t\"errors.New.text\":                        0,\n\t\"fmt.Fprintf.format\":                     1,\n\t\"fmt.Printf.format\":                      0,\n\t\"fmt.Sprintf.a[0]\":                       1,\n\t\"fmt.Sprintf.format\":                     0,\n\t\"json.Marshal.v\":                         0,\n\t\"json.Unmarshal.v\":                       1,\n\t\"len.v\":                                  0,\n\t\"make.size[0]\":                           1,\n\t\"make.size[1]\":                           2,\n\t\"make.t\":                                 0,\n\t\"net/url.Parse.rawurl\":                   0,\n\t\"os.OpenFile.flag\":                       1,\n\t\"os/exec.Command.name\":                   0,\n\t\"os/signal.Notify.c\":                     0,\n\t\"regexp.Compile.expr\":                    0,\n\t\"runtime.SetFinalizer.finalizer\":         1,\n\t\"runtime.SetFinalizer.obj\":               0,\n\t\"sort.Sort.data\":                         0,\n\t\"strconv.AppendFloat.bitSize\":            4,\n\t\"strconv.AppendFloat.fmt\":                2,\n\t\"strconv.AppendInt.base\":                 2,\n\t\"strconv.AppendUint.base\":                2,\n\t\"strconv.FormatComplex.bitSize\":          3,\n\t\"strconv.FormatComplex.fmt\":              1,\n\t\"strconv.FormatFloat.bitSize\":            3,\n\t\"strconv.FormatFloat.fmt\":                1,\n\t\"strconv.FormatInt.base\":                 1,\n\t\"strconv.FormatUint.base\":                1,\n\t\"strconv.ParseComplex.bitSize\":           1,\n\t\"strconv.ParseFloat.bitSize\":             1,\n\t\"strconv.ParseInt.base\":                  1,\n\t\"strconv.ParseInt.bitSize\":               2,\n\t\"strconv.ParseUint.base\":                 1,\n\t\"strconv.ParseUint.bitSize\":              2,\n\t\"time.Parse.layout\":                      0,\n\t\"time.Sleep.d\":                           0,\n\t\"xml.Marshal.v\":                          0,\n\t\"xml.Unmarshal.v\":                        1,\n}\n\n// Arg turns the name of an argument into an argument index.\n// Indices are zero-based and method receivers do not count as arguments.\n//\n// Arg refers to a manually compiled mapping (see the Args variable.)\n// Modify the knowledge package to add new arguments.\nfunc Arg(name string) int {\n\tn, ok := Args[name]\n\tif !ok {\n\t\tpanic(\"unknown argument \" + name)\n\t}\n\treturn n\n}\n"
  },
  {
    "path": "knowledge/deprecated.go",
    "content": "package knowledge\n\nconst (\n\t// DeprecatedNeverUse indicates that an API should never be used, regardless of Go version.\n\tDeprecatedNeverUse = \"never\"\n\t// DeprecatedUseNoLonger indicates that an API has no use anymore.\n\tDeprecatedUseNoLonger = \"no longer\"\n)\n\n// Deprecation describes when a Go API has been deprecated.\ntype Deprecation struct {\n\t// The minor Go version since which this API has been deprecated.\n\tDeprecatedSince string\n\t// The minor Go version since which an alternative API has been available.\n\t// May also be one of DeprecatedNeverUse or DeprecatedUseNoLonger.\n\tAlternativeAvailableSince string\n}\n\n// go/importer.ForCompiler contains \"Deprecated:\", but it refers to a single argument, not the whole function.\n// Luckily, the notice starts in the middle of a paragraph, and as such isn't detected by us.\n\n// TODO(dh): StdlibDeprecations doesn't contain entries for internal packages and unexported API. That's fine for normal\n// users, but makes the Deprecated check less useful for people working on Go itself.\n\n// StdlibDeprecations contains a mapping of Go API (such as variables, methods, or fields, among others)\n// to information about when it has been deprecated.\nvar StdlibDeprecations = map[string]Deprecation{\n\t// FIXME(dh): AllowBinary isn't being detected as deprecated\n\t// because the comment has a newline right after \"Deprecated:\"\n\t\"go/build.AllowBinary\":                      {\"go1.7\", \"go1.7\"},\n\t\"(archive/zip.FileHeader).CompressedSize\":   {\"go1.1\", \"go1.1\"},\n\t\"(archive/zip.FileHeader).UncompressedSize\": {\"go1.1\", \"go1.1\"},\n\t\"(archive/zip.FileHeader).ModifiedTime\":     {\"go1.10\", \"go1.10\"},\n\t\"(archive/zip.FileHeader).ModifiedDate\":     {\"go1.10\", \"go1.10\"},\n\t\"(*archive/zip.FileHeader).ModTime\":         {\"go1.10\", \"go1.10\"},\n\t\"(*archive/zip.FileHeader).SetModTime\":      {\"go1.10\", \"go1.10\"},\n\t\"(go/doc.Package).Bugs\":                     {\"go1.1\", \"go1.1\"},\n\t\"os.SEEK_SET\":                               {\"go1.7\", \"go1.7\"},\n\t\"os.SEEK_CUR\":                               {\"go1.7\", \"go1.7\"},\n\t\"os.SEEK_END\":                               {\"go1.7\", \"go1.7\"},\n\t\"(net.Dialer).Cancel\":                       {\"go1.7\", \"go1.7\"},\n\t\"runtime.CPUProfile\":                        {\"go1.9\", \"go1.0\"},\n\t\"compress/flate.ReadError\":                  {\"go1.6\", DeprecatedUseNoLonger},\n\t\"compress/flate.WriteError\":                 {\"go1.6\", DeprecatedUseNoLonger},\n\t\"path/filepath.HasPrefix\":                   {\"go1.0\", DeprecatedNeverUse},\n\t\"(net/http.Transport).Dial\":                 {\"go1.7\", \"go1.7\"},\n\t\"(net/http.Transport).DialTLS\":              {\"go1.14\", \"go1.14\"},\n\t\"(*net/http.Transport).CancelRequest\":       {\"go1.6\", \"go1.5\"},\n\t\"net/http.ErrWriteAfterFlush\":               {\"go1.7\", DeprecatedUseNoLonger},\n\t\"net/http.ErrHeaderTooLong\":                 {\"go1.8\", DeprecatedUseNoLonger},\n\t\"net/http.ErrShortBody\":                     {\"go1.8\", DeprecatedUseNoLonger},\n\t\"net/http.ErrMissingContentLength\":          {\"go1.8\", DeprecatedUseNoLonger},\n\t\"net/http/httputil.ErrPersistEOF\":           {\"go1.0\", DeprecatedUseNoLonger},\n\t\"net/http/httputil.ErrClosed\":               {\"go1.0\", DeprecatedUseNoLonger},\n\t\"net/http/httputil.ErrPipeline\":             {\"go1.0\", DeprecatedUseNoLonger},\n\t\"net/http/httputil.ServerConn\":              {\"go1.0\", \"go1.0\"},\n\t\"net/http/httputil.NewServerConn\":           {\"go1.0\", \"go1.0\"},\n\t\"net/http/httputil.ClientConn\":              {\"go1.0\", \"go1.0\"},\n\t\"net/http/httputil.NewClientConn\":           {\"go1.0\", \"go1.0\"},\n\t\"net/http/httputil.NewProxyClientConn\":      {\"go1.0\", \"go1.0\"},\n\t\"(net/http.Request).Cancel\":                 {\"go1.7\", \"go1.7\"},\n\t\"(text/template/parse.PipeNode).Line\":       {\"go1.1\", DeprecatedUseNoLonger},\n\t\"(text/template/parse.ActionNode).Line\":     {\"go1.1\", DeprecatedUseNoLonger},\n\t\"(text/template/parse.BranchNode).Line\":     {\"go1.1\", DeprecatedUseNoLonger},\n\t\"(text/template/parse.TemplateNode).Line\":   {\"go1.1\", DeprecatedUseNoLonger},\n\t\"database/sql/driver.ColumnConverter\":       {\"go1.9\", \"go1.9\"},\n\t\"database/sql/driver.Execer\":                {\"go1.8\", \"go1.8\"},\n\t\"database/sql/driver.Queryer\":               {\"go1.8\", \"go1.8\"},\n\t\"(database/sql/driver.Conn).Begin\":          {\"go1.8\", \"go1.8\"},\n\t\"(database/sql/driver.Stmt).Exec\":           {\"go1.8\", \"go1.8\"},\n\t\"(database/sql/driver.Stmt).Query\":          {\"go1.8\", \"go1.8\"},\n\t\"syscall.StringByteSlice\":                   {\"go1.1\", \"go1.1\"},\n\t\"syscall.StringBytePtr\":                     {\"go1.1\", \"go1.1\"},\n\t\"syscall.StringSlicePtr\":                    {\"go1.1\", \"go1.1\"},\n\t\"syscall.StringToUTF16\":                     {\"go1.1\", \"go1.1\"},\n\t\"syscall.StringToUTF16Ptr\":                  {\"go1.1\", \"go1.1\"},\n\t\"(*regexp.Regexp).Copy\":                     {\"go1.12\", DeprecatedUseNoLonger},\n\t\"(archive/tar.Header).Xattrs\":               {\"go1.10\", \"go1.10\"},\n\t\"archive/tar.TypeRegA\":                      {\"go1.11\", \"go1.1\"},\n\t\"go/types.NewInterface\":                     {\"go1.11\", \"go1.11\"},\n\t\"(*go/types.Interface).Embedded\":            {\"go1.11\", \"go1.11\"},\n\t\"go/importer.For\":                           {\"go1.12\", \"go1.12\"},\n\t\"encoding/json.InvalidUTF8Error\":            {\"go1.2\", DeprecatedUseNoLonger},\n\t\"encoding/json.UnmarshalFieldError\":         {\"go1.2\", DeprecatedUseNoLonger},\n\t\"encoding/csv.ErrTrailingComma\":             {\"go1.2\", DeprecatedUseNoLonger},\n\t\"(encoding/csv.Reader).TrailingComma\":       {\"go1.2\", DeprecatedUseNoLonger},\n\t\"(net.Dialer).DualStack\":                    {\"go1.12\", \"go1.12\"},\n\t\"net/http.ErrUnexpectedTrailer\":             {\"go1.12\", DeprecatedUseNoLonger},\n\t\"net/http.CloseNotifier\":                    {\"go1.11\", \"go1.7\"},\n\t// This is hairy. The notice says \"Not all errors in the http package related to protocol errors are of type ProtocolError\", but doesn't that imply that some errors do?\n\t\"net/http.ProtocolError\":                       {\"go1.8\", DeprecatedUseNoLonger},\n\t\"(crypto/x509.CertificateRequest).Attributes\":  {\"go1.5\", \"go1.3\"},\n\t\"(*crypto/x509.Certificate).CheckCRLSignature\": {\"go1.19\", \"go1.19\"},\n\t\"crypto/x509.ParseCRL\":                         {\"go1.19\", \"go1.19\"},\n\t\"crypto/x509.ParseDERCRL\":                      {\"go1.19\", \"go1.19\"},\n\t\"(*crypto/x509.Certificate).CreateCRL\":         {\"go1.19\", \"go1.19\"},\n\t\"crypto/x509/pkix.TBSCertificateList\":          {\"go1.19\", \"go1.19\"},\n\t\"crypto/x509/pkix.RevokedCertificate\":          {\"go1.19\", \"go1.19\"},\n\t\"go/doc.ToHTML\":                                {\"go1.20\", \"go1.20\"},\n\t\"go/doc.ToText\":                                {\"go1.20\", \"go1.20\"},\n\t\"go/doc.Synopsis\":                              {\"go1.20\", \"go1.20\"},\n\t\"math/rand.Seed\":                               {\"go1.20\", \"go1.0\"},\n\t\"math/rand.Read\":                               {\"go1.20\", DeprecatedNeverUse},\n\n\t// These functions have no direct alternative, but they are insecure and should no longer be used.\n\t\"crypto/x509.IsEncryptedPEMBlock\": {\"go1.16\", DeprecatedNeverUse},\n\t\"crypto/x509.DecryptPEMBlock\":     {\"go1.16\", DeprecatedNeverUse},\n\t\"crypto/x509.EncryptPEMBlock\":     {\"go1.16\", DeprecatedNeverUse},\n\t\"crypto/dsa\":                      {\"go1.16\", DeprecatedNeverUse},\n\n\t// This function has no alternative, but also no purpose.\n\t\"(*crypto/rc4.Cipher).Reset\":                     {\"go1.12\", DeprecatedNeverUse},\n\t\"(net/http/httptest.ResponseRecorder).HeaderMap\": {\"go1.11\", \"go1.7\"},\n\t\"image.ZP\":                                    {\"go1.13\", \"go1.0\"},\n\t\"image.ZR\":                                    {\"go1.13\", \"go1.0\"},\n\t\"(*debug/gosym.LineTable).LineToPC\":           {\"go1.2\", \"go1.2\"},\n\t\"(*debug/gosym.LineTable).PCToLine\":           {\"go1.2\", \"go1.2\"},\n\t\"crypto/tls.VersionSSL30\":                     {\"go1.13\", DeprecatedNeverUse},\n\t\"(crypto/tls.Config).NameToCertificate\":       {\"go1.14\", DeprecatedUseNoLonger},\n\t\"(*crypto/tls.Config).BuildNameToCertificate\": {\"go1.14\", DeprecatedUseNoLonger},\n\t\"(crypto/tls.Config).SessionTicketKey\":        {\"go1.16\", \"go1.5\"},\n\t// No alternative, no use\n\t\"(crypto/tls.ConnectionState).NegotiatedProtocolIsMutual\": {\"go1.16\", DeprecatedNeverUse},\n\t// No alternative, but insecure\n\t\"(crypto/tls.ConnectionState).TLSUnique\": {\"go1.16\", DeprecatedNeverUse},\n\t\"image/jpeg.Reader\":                      {\"go1.4\", DeprecatedNeverUse},\n\n\t// All of these have been deprecated in favour of external libraries\n\t\"syscall.AttachLsf\":                     {\"go1.7\", \"go1.0\"},\n\t\"syscall.DetachLsf\":                     {\"go1.7\", \"go1.0\"},\n\t\"syscall.LsfSocket\":                     {\"go1.7\", \"go1.0\"},\n\t\"syscall.SetLsfPromisc\":                 {\"go1.7\", \"go1.0\"},\n\t\"syscall.LsfJump\":                       {\"go1.7\", \"go1.0\"},\n\t\"syscall.LsfStmt\":                       {\"go1.7\", \"go1.0\"},\n\t\"syscall.BpfStmt\":                       {\"go1.7\", \"go1.0\"},\n\t\"syscall.BpfJump\":                       {\"go1.7\", \"go1.0\"},\n\t\"syscall.BpfBuflen\":                     {\"go1.7\", \"go1.0\"},\n\t\"syscall.SetBpfBuflen\":                  {\"go1.7\", \"go1.0\"},\n\t\"syscall.BpfDatalink\":                   {\"go1.7\", \"go1.0\"},\n\t\"syscall.SetBpfDatalink\":                {\"go1.7\", \"go1.0\"},\n\t\"syscall.SetBpfPromisc\":                 {\"go1.7\", \"go1.0\"},\n\t\"syscall.FlushBpf\":                      {\"go1.7\", \"go1.0\"},\n\t\"syscall.BpfInterface\":                  {\"go1.7\", \"go1.0\"},\n\t\"syscall.SetBpfInterface\":               {\"go1.7\", \"go1.0\"},\n\t\"syscall.BpfTimeout\":                    {\"go1.7\", \"go1.0\"},\n\t\"syscall.SetBpfTimeout\":                 {\"go1.7\", \"go1.0\"},\n\t\"syscall.BpfStats\":                      {\"go1.7\", \"go1.0\"},\n\t\"syscall.SetBpfImmediate\":               {\"go1.7\", \"go1.0\"},\n\t\"syscall.SetBpf\":                        {\"go1.7\", \"go1.0\"},\n\t\"syscall.CheckBpfVersion\":               {\"go1.7\", \"go1.0\"},\n\t\"syscall.BpfHeadercmpl\":                 {\"go1.7\", \"go1.0\"},\n\t\"syscall.SetBpfHeadercmpl\":              {\"go1.7\", \"go1.0\"},\n\t\"syscall.RouteRIB\":                      {\"go1.8\", \"go1.0\"},\n\t\"syscall.RoutingMessage\":                {\"go1.8\", \"go1.0\"},\n\t\"syscall.RouteMessage\":                  {\"go1.8\", \"go1.0\"},\n\t\"syscall.InterfaceMessage\":              {\"go1.8\", \"go1.0\"},\n\t\"syscall.InterfaceAddrMessage\":          {\"go1.8\", \"go1.0\"},\n\t\"syscall.ParseRoutingMessage\":           {\"go1.8\", \"go1.0\"},\n\t\"syscall.ParseRoutingSockaddr\":          {\"go1.8\", \"go1.0\"},\n\t\"syscall.InterfaceAnnounceMessage\":      {\"go1.7\", \"go1.0\"},\n\t\"syscall.InterfaceMulticastAddrMessage\": {\"go1.7\", \"go1.0\"},\n\t\"syscall.FormatMessage\":                 {\"go1.5\", \"go1.0\"},\n\t\"syscall.PostQueuedCompletionStatus\":    {\"go1.17\", \"go1.0\"},\n\t\"syscall.GetQueuedCompletionStatus\":     {\"go1.17\", \"go1.0\"},\n\t\"syscall.CreateIoCompletionPort\":        {\"go1.17\", \"go1.0\"},\n\n\t// We choose to only track the package itself, even though all functions are deprecated individually, too. Anyone\n\t// using ioutil directly will have to import it, and this keeps the noise down.\n\t\"io/ioutil\": {\"go1.19\", \"go1.19\"},\n\n\t\"bytes.Title\":   {\"go1.18\", \"go1.0\"},\n\t\"strings.Title\": {\"go1.18\", \"go1.0\"},\n\t\"(crypto/tls.Config).PreferServerCipherSuites\": {\"go1.18\", DeprecatedUseNoLonger},\n\t// It's not clear if Subjects was okay to use in the past, so we err on the less noisy side of assuming that it was.\n\t\"(*crypto/x509.CertPool).Subjects\": {\"go1.18\", DeprecatedUseNoLonger},\n\t\"go/types.NewSignature\":            {\"go1.18\", \"go1.18\"},\n\t\"(net.Error).Temporary\":            {\"go1.18\", DeprecatedNeverUse},\n\t// InterfaceData is another tricky case. It was deprecated in Go 1.18, but has been useless since Go 1.4, and an\n\t// \"alternative\" (using your own unsafe hacks) has existed forever. We don't want to get into hairsplitting with\n\t// users who somehow successfully used this between 1.4 and 1.18, so we'll just tag it as deprecated since 1.18.\n\t\"(reflect.Value).InterfaceData\": {\"go1.18\", \"go1.18\"},\n\n\t// The following objects are only deprecated on Windows.\n\t\"syscall.Syscall\":   {\"go1.18\", \"go1.18\"},\n\t\"syscall.Syscall12\": {\"go1.18\", \"go1.18\"},\n\t\"syscall.Syscall15\": {\"go1.18\", \"go1.18\"},\n\t\"syscall.Syscall18\": {\"go1.18\", \"go1.18\"},\n\t\"syscall.Syscall6\":  {\"go1.18\", \"go1.18\"},\n\t\"syscall.Syscall9\":  {\"go1.18\", \"go1.18\"},\n\n\t\"reflect.SliceHeader\":                           {\"go1.21\", \"go1.17\"},\n\t\"reflect.StringHeader\":                          {\"go1.21\", \"go1.20\"},\n\t\"crypto/elliptic.GenerateKey\":                   {\"go1.21\", \"go1.21\"},\n\t\"crypto/elliptic.Marshal\":                       {\"go1.21\", \"go1.21\"},\n\t\"crypto/elliptic.Unmarshal\":                     {\"go1.21\", \"go1.21\"},\n\t\"(*crypto/elliptic.CurveParams).Add\":            {\"go1.21\", \"go1.21\"},\n\t\"(*crypto/elliptic.CurveParams).Double\":         {\"go1.21\", \"go1.21\"},\n\t\"(*crypto/elliptic.CurveParams).IsOnCurve\":      {\"go1.21\", \"go1.21\"},\n\t\"(*crypto/elliptic.CurveParams).ScalarBaseMult\": {\"go1.21\", \"go1.21\"},\n\t\"(*crypto/elliptic.CurveParams).ScalarMult\":     {\"go1.21\", \"go1.21\"},\n\t\"(crypto/elliptic.Curve).Add\":                   {\"go1.21\", \"go1.21\"},\n\t\"(crypto/elliptic.Curve).Double\":                {\"go1.21\", \"go1.21\"},\n\t\"(crypto/elliptic.Curve).IsOnCurve\":             {\"go1.21\", \"go1.21\"},\n\t\"(crypto/elliptic.Curve).ScalarBaseMult\":        {\"go1.21\", \"go1.21\"},\n\t\"(crypto/elliptic.Curve).ScalarMult\":            {\"go1.21\", \"go1.21\"},\n\n\t\"crypto/rsa.GenerateMultiPrimeKey\":                 {\"go1.21\", DeprecatedNeverUse},\n\t\"(crypto/rsa.PrecomputedValues).CRTValues\":         {\"go1.21\", DeprecatedNeverUse},\n\t\"(crypto/x509.RevocationList).RevokedCertificates\": {\"go1.21\", \"go1.21\"},\n\n\t\"go/ast.NewPackage\":           {\"go1.22\", \"go1.0\"},\n\t\"go/ast.Importer\":             {\"go1.22\", \"go1.0\"},\n\t\"go/ast.Object\":               {\"go1.22\", \"go1.0\"},\n\t\"go/ast.Package\":              {\"go1.22\", \"go1.0\"},\n\t\"go/ast.Scope\":                {\"go1.22\", \"go1.0\"},\n\t\"html/template.ErrJSTemplate\": {\"go1.22\", DeprecatedUseNoLonger},\n\t\"reflect.PtrTo\":               {\"go1.22\", \"go1.18\"},\n\n\t// Technically, runtime.GOROOT could be considered DeprecatedNeverUse, but\n\t// using it used to be a lot more common and accepted.\n\t\"runtime.GOROOT\": {\"go1.24\", DeprecatedUseNoLonger},\n\t// These are never safe to use; a concrete alternative was added in Go 1.2 (crypto/cipher.AEAD).\n\t\"crypto/cipher.NewCFBDecrypter\": {\"go1.24\", \"go1.2\"},\n\t\"crypto/cipher.NewCFBEncrypter\": {\"go1.24\", \"go1.2\"},\n\t\"crypto/cipher.NewOFB\":          {\"go1.24\", \"go1.2\"},\n\n\t\"go/ast.FilterFuncDuplicates\":       {\"go1.25\", \"go1.0\"},\n\t\"go/ast.FilterImportDuplicates\":     {\"go1.25\", \"go1.0\"},\n\t\"go/ast.FilterUnassociatedComments\": {\"go1.25\", \"go1.0\"},\n\t\"go/ast.FilterPackage\":              {\"go1.25\", \"go1.0\"},\n\t\"go/ast.MergePackageFiles\":          {\"go1.25\", \"go1.0\"},\n\t\"go/ast.PackageExports\":             {\"go1.25\", \"go1.0\"},\n\t\"go/ast.MergeMode\":                  {\"go1.25\", \"go1.0\"},\n\t// Go 1.11 because that's around the time x/tools/go/packages was released.\n\t\"go/parser.ParseDir\": {\"go1.25\", \"go1.11\"},\n\n\t// Go 1.25 is the first version to provide all of the alternatives mentioned\n\t// by the deprecation note.\n\t\"(crypto/ecdsa.PublicKey).X\":  {\"go1.26\", \"go1.25\"},\n\t\"(crypto/ecdsa.PublicKey).Y\":  {\"go1.26\", \"go1.25\"},\n\t\"(crypto/ecdsa.PrivateKey).D\": {\"go1.26\", \"go1.25\"},\n\n\t\"crypto/rsa.DecryptPKCS1v15\":           {\"go1.26\", DeprecatedNeverUse},\n\t\"crypto/rsa.DecryptPKCS1v15SessionKey\": {\"go1.26\", DeprecatedNeverUse},\n\t\"crypto/rsa.PKCS1v15DecryptOptions\":    {\"go1.26\", DeprecatedNeverUse},\n\t\"crypto/rsa.EncryptPKCS1v15\":           {\"go1.26\", DeprecatedNeverUse},\n\n\t\"(net/http/httputil.ReverseProxy).Director\": {\"go1.26\", \"go1.20\"},\n}\n\n// Last imported from GOROOT/api/go1.26.txt at d3ddc4854429185e6e06ca1f7628bb790404abb5.\n"
  },
  {
    "path": "knowledge/doc.go",
    "content": "// Package knowledge contains manually collected information about Go APIs.\npackage knowledge\n"
  },
  {
    "path": "knowledge/signatures.go",
    "content": "package knowledge\n\nimport (\n\t\"go/token\"\n\t\"go/types\"\n)\n\nvar Signatures = map[string]*types.Signature{\n\t\"(io.Seeker).Seek\": types.NewSignatureType(nil, nil, nil,\n\t\ttypes.NewTuple(\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Typ[types.Int64]),\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Typ[types.Int]),\n\t\t),\n\t\ttypes.NewTuple(\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Typ[types.Int64]),\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Universe.Lookup(\"error\").Type()),\n\t\t),\n\t\tfalse,\n\t),\n\n\t\"(io.Writer).Write\": types.NewSignatureType(nil, nil, nil,\n\t\ttypes.NewTuple(\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.NewSlice(types.Typ[types.Byte])),\n\t\t),\n\t\ttypes.NewTuple(\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Typ[types.Int]),\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Universe.Lookup(\"error\").Type()),\n\t\t),\n\t\tfalse,\n\t),\n\n\t\"(io.StringWriter).WriteString\": types.NewSignatureType(nil, nil, nil,\n\t\ttypes.NewTuple(\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Typ[types.String]),\n\t\t),\n\t\ttypes.NewTuple(\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Typ[types.Int]),\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Universe.Lookup(\"error\").Type()),\n\t\t),\n\t\tfalse,\n\t),\n\n\t\"(encoding.TextMarshaler).MarshalText\": types.NewSignatureType(nil, nil, nil,\n\t\ttypes.NewTuple(),\n\t\ttypes.NewTuple(\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.NewSlice(types.Typ[types.Byte])),\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Universe.Lookup(\"error\").Type()),\n\t\t),\n\t\tfalse,\n\t),\n\n\t\"(encoding/json.Marshaler).MarshalJSON\": types.NewSignatureType(nil, nil, nil,\n\t\ttypes.NewTuple(),\n\t\ttypes.NewTuple(\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.NewSlice(types.Typ[types.Byte])),\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Universe.Lookup(\"error\").Type()),\n\t\t),\n\t\tfalse,\n\t),\n\n\t\"(fmt.Stringer).String\": types.NewSignatureType(nil, nil, nil,\n\t\ttypes.NewTuple(),\n\t\ttypes.NewTuple(\n\t\t\ttypes.NewParam(token.NoPos, nil, \"\", types.Typ[types.String]),\n\t\t),\n\t\tfalse,\n\t),\n}\n\nvar Interfaces = map[string]*types.Interface{\n\t\"fmt.Stringer\": types.NewInterfaceType(\n\t\t[]*types.Func{\n\t\t\ttypes.NewFunc(token.NoPos, nil, \"String\", Signatures[\"(fmt.Stringer).String\"]),\n\t\t},\n\t\tnil,\n\t).Complete(),\n\n\t\"error\": types.Universe.Lookup(\"error\").Type().Underlying().(*types.Interface),\n\n\t\"io.Writer\": types.NewInterfaceType(\n\t\t[]*types.Func{\n\t\t\ttypes.NewFunc(token.NoPos, nil, \"Write\", Signatures[\"(io.Writer).Write\"]),\n\t\t},\n\t\tnil,\n\t).Complete(),\n\n\t\"io.StringWriter\": types.NewInterfaceType(\n\t\t[]*types.Func{\n\t\t\ttypes.NewFunc(token.NoPos, nil, \"WriteString\", Signatures[\"(io.StringWriter).WriteString\"]),\n\t\t},\n\t\tnil,\n\t).Complete(),\n\n\t\"encoding.TextMarshaler\": types.NewInterfaceType(\n\t\t[]*types.Func{\n\t\t\ttypes.NewFunc(token.NoPos, nil, \"MarshalText\", Signatures[\"(encoding.TextMarshaler).MarshalText\"]),\n\t\t},\n\t\tnil,\n\t).Complete(),\n\n\t\"encoding/json.Marshaler\": types.NewInterfaceType(\n\t\t[]*types.Func{\n\t\t\ttypes.NewFunc(token.NoPos, nil, \"MarshalJSON\", Signatures[\"(encoding/json.Marshaler).MarshalJSON\"]),\n\t\t},\n\t\tnil,\n\t).Complete(),\n}\n"
  },
  {
    "path": "knowledge/targets.go",
    "content": "package knowledge\n\nvar KnownGOOS = map[string]struct{}{\n\t\"aix\":       {},\n\t\"android\":   {},\n\t\"darwin\":    {},\n\t\"dragonfly\": {},\n\t\"freebsd\":   {},\n\t\"hurd\":      {},\n\t\"illumos\":   {},\n\t\"ios\":       {},\n\t\"js\":        {},\n\t\"linux\":     {},\n\t\"netbsd\":    {},\n\t\"openbsd\":   {},\n\t\"plan9\":     {},\n\t\"solaris\":   {},\n\t\"wasip1\":    {},\n\t\"windows\":   {},\n}\n\nvar KnownGOARCH = map[string]struct{}{\n\t\"386\":      {},\n\t\"amd64\":    {},\n\t\"arm\":      {},\n\t\"arm64\":    {},\n\t\"loong64\":  {},\n\t\"mips\":     {},\n\t\"mipsle\":   {},\n\t\"mips64\":   {},\n\t\"mips64le\": {},\n\t\"ppc64\":    {},\n\t\"ppc64le\":  {},\n\t\"riscv64\":  {},\n\t\"s390x\":    {},\n\t\"sparc64\":  {},\n\t\"wasm\":     {},\n}\n"
  },
  {
    "path": "lintcmd/cache/UPSTREAM",
    "content": "This package is a copy of cmd/go/internal/cache.\n\nDifferences from upstream:\n- we continue to use renameio instead of lockedfile for writing trim.txt\n- we still use I/O helpers that work with earlier versions of Go.\n- we use a cache directory specific to Staticcheck\n- we use a Staticcheck-specific salt\n\nThe last upstream commit we've looked at was:\n06ac303f6a14b133254f757e54599c48e3c2a4ad\n"
  },
  {
    "path": "lintcmd/cache/cache.go",
    "content": "// Copyright 2017 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package cache implements a build artifact cache.\n//\n// This package is a slightly modified fork of Go's\n// cmd/go/internal/cache package.\npackage cache\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"honnef.co/go/tools/internal/renameio\"\n)\n\n// An ActionID is a cache action key, the hash of a complete description of a\n// repeatable computation (command line, environment variables,\n// input file contents, executable contents).\ntype ActionID [HashSize]byte\n\n// An OutputID is a cache output key, the hash of an output of a computation.\ntype OutputID [HashSize]byte\n\n// A Cache is a package cache, backed by a file system directory tree.\ntype Cache struct {\n\tdir  string\n\tnow  func() time.Time\n\tsalt []byte\n}\n\n// Open opens and returns the cache in the given directory.\n//\n// It is safe for multiple processes on a single machine to use the\n// same cache directory in a local file system simultaneously.\n// They will coordinate using operating system file locks and may\n// duplicate effort but will not corrupt the cache.\n//\n// However, it is NOT safe for multiple processes on different machines\n// to share a cache directory (for example, if the directory were stored\n// in a network file system). File locking is notoriously unreliable in\n// network file systems and may not suffice to protect the cache.\nfunc Open(dir string) (*Cache, error) {\n\tinfo, err := os.Stat(dir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !info.IsDir() {\n\t\treturn nil, &os.PathError{Op: \"open\", Path: dir, Err: fmt.Errorf(\"not a directory\")}\n\t}\n\tfor i := range 256 {\n\t\tname := filepath.Join(dir, fmt.Sprintf(\"%02x\", i))\n\t\tif err := os.MkdirAll(name, 0777); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tc := &Cache{\n\t\tdir: dir,\n\t\tnow: time.Now,\n\t}\n\treturn c, nil\n}\n\nfunc (c *Cache) SetSalt(b []byte) {\n\tc.salt = b\n}\n\n// fileName returns the name of the file corresponding to the given id.\nfunc (c *Cache) fileName(id [HashSize]byte, key string) string {\n\treturn filepath.Join(c.dir, fmt.Sprintf(\"%02x\", id[0]), fmt.Sprintf(\"%x\", id)+\"-\"+key)\n}\n\n// An entryNotFoundError indicates that a cache entry was not found, with an\n// optional underlying reason.\ntype entryNotFoundError struct {\n\tErr error\n}\n\nfunc (e *entryNotFoundError) Error() string {\n\tif e.Err == nil {\n\t\treturn \"cache entry not found\"\n\t}\n\treturn fmt.Sprintf(\"cache entry not found: %v\", e.Err)\n}\n\nfunc (e *entryNotFoundError) Unwrap() error {\n\treturn e.Err\n}\n\nconst (\n\t// action entry file is \"v1 <hex id> <hex out> <decimal size space-padded to 20 bytes> <unixnano space-padded to 20 bytes>\\n\"\n\thexSize   = HashSize * 2\n\tentrySize = 2 + 1 + hexSize + 1 + hexSize + 1 + 20 + 1 + 20 + 1\n)\n\n// verify controls whether to run the cache in verify mode.\n// In verify mode, the cache always returns errMissing from Get\n// but then double-checks in Put that the data being written\n// exactly matches any existing entry. This provides an easy\n// way to detect program behavior that would have been different\n// had the cache entry been returned from Get.\n//\n// verify is enabled by setting the environment variable\n// GODEBUG=gocacheverify=1.\nvar verify = false\n\nvar errVerifyMode = errors.New(\"gocacheverify=1\")\n\n// DebugTest is set when GODEBUG=gocachetest=1 is in the environment.\nvar DebugTest = false\n\nfunc init() { initEnv() }\n\nfunc initEnv() {\n\tverify = false\n\tdebugHash = false\n\tdebug := strings.SplitSeq(os.Getenv(\"GODEBUG\"), \",\")\n\tfor f := range debug {\n\t\tif f == \"gocacheverify=1\" {\n\t\t\tverify = true\n\t\t}\n\t\tif f == \"gocachehash=1\" {\n\t\t\tdebugHash = true\n\t\t}\n\t\tif f == \"gocachetest=1\" {\n\t\t\tDebugTest = true\n\t\t}\n\t}\n}\n\n// Get looks up the action ID in the cache,\n// returning the corresponding output ID and file size, if any.\n// Note that finding an output ID does not guarantee that the\n// saved file for that output ID is still available.\nfunc (c *Cache) Get(id ActionID) (Entry, error) {\n\tif verify {\n\t\treturn Entry{}, &entryNotFoundError{Err: errVerifyMode}\n\t}\n\treturn c.get(id)\n}\n\ntype Entry struct {\n\tOutputID OutputID\n\tSize     int64\n\tTime     time.Time\n}\n\n// get is Get but does not respect verify mode, so that Put can use it.\nfunc (c *Cache) get(id ActionID) (Entry, error) {\n\tmissing := func(reason error) (Entry, error) {\n\t\treturn Entry{}, &entryNotFoundError{Err: reason}\n\t}\n\tf, err := os.Open(c.fileName(id, \"a\"))\n\tif err != nil {\n\t\treturn missing(err)\n\t}\n\tdefer f.Close()\n\tentry := make([]byte, entrySize+1) // +1 to detect whether f is too long\n\tif n, err := io.ReadFull(f, entry); n > entrySize {\n\t\treturn missing(errors.New(\"too long\"))\n\t} else if err != io.ErrUnexpectedEOF {\n\t\tif err == io.EOF {\n\t\t\treturn missing(errors.New(\"file is empty\"))\n\t\t}\n\t\treturn missing(err)\n\t} else if n < entrySize {\n\t\treturn missing(errors.New(\"entry file incomplete\"))\n\t}\n\tif entry[0] != 'v' || entry[1] != '1' || entry[2] != ' ' || entry[3+hexSize] != ' ' || entry[3+hexSize+1+hexSize] != ' ' || entry[3+hexSize+1+hexSize+1+20] != ' ' || entry[entrySize-1] != '\\n' {\n\t\treturn missing(errors.New(\"invalid header\"))\n\t}\n\teid, entry := entry[3:3+hexSize], entry[3+hexSize:]\n\teout, entry := entry[1:1+hexSize], entry[1+hexSize:]\n\tesize, entry := entry[1:1+20], entry[1+20:]\n\t//lint:ignore SA4006 See https://github.com/dominikh/go-tools/issues/465\n\tetime, entry := entry[1:1+20], entry[1+20:]\n\tvar buf [HashSize]byte\n\tif _, err := hex.Decode(buf[:], eid); err != nil {\n\t\treturn missing(fmt.Errorf(\"decoding ID: %v\", err))\n\t} else if buf != id {\n\t\treturn missing(errors.New(\"mismatched ID\"))\n\t}\n\tif _, err := hex.Decode(buf[:], eout); err != nil {\n\t\treturn missing(fmt.Errorf(\"decoding output ID: %v\", err))\n\t}\n\ti := 0\n\tfor i < len(esize) && esize[i] == ' ' {\n\t\ti++\n\t}\n\tsize, err := strconv.ParseInt(string(esize[i:]), 10, 64)\n\tif err != nil {\n\t\treturn missing(fmt.Errorf(\"parsing size: %v\", err))\n\t} else if size < 0 {\n\t\treturn missing(errors.New(\"negative size\"))\n\t}\n\ti = 0\n\tfor i < len(etime) && etime[i] == ' ' {\n\t\ti++\n\t}\n\ttm, err := strconv.ParseInt(string(etime[i:]), 10, 64)\n\tif err != nil {\n\t\treturn missing(fmt.Errorf(\"parsing timestamp: %v\", err))\n\t} else if tm < 0 {\n\t\treturn missing(errors.New(\"negative timestamp\"))\n\t}\n\n\tc.used(c.fileName(id, \"a\"))\n\n\treturn Entry{buf, size, time.Unix(0, tm)}, nil\n}\n\n// GetFile looks up the action ID in the cache and returns\n// the name of the corresponding data file.\nfunc (c *Cache) GetFile(id ActionID) (file string, entry Entry, err error) {\n\tentry, err = c.Get(id)\n\tif err != nil {\n\t\treturn \"\", Entry{}, err\n\t}\n\tfile = c.OutputFile(entry.OutputID)\n\tinfo, err := os.Stat(file)\n\tif err != nil {\n\t\treturn \"\", Entry{}, &entryNotFoundError{Err: err}\n\t}\n\tif info.Size() != entry.Size {\n\t\treturn \"\", Entry{}, &entryNotFoundError{Err: errors.New(\"file incomplete\")}\n\t}\n\treturn file, entry, nil\n}\n\n// GetBytes looks up the action ID in the cache and returns\n// the corresponding output bytes.\n// GetBytes should only be used for data that can be expected to fit in memory.\nfunc (c *Cache) GetBytes(id ActionID) ([]byte, Entry, error) {\n\tentry, err := c.Get(id)\n\tif err != nil {\n\t\treturn nil, entry, err\n\t}\n\tdata, _ := os.ReadFile(c.OutputFile(entry.OutputID))\n\tif sha256.Sum256(data) != entry.OutputID {\n\t\treturn nil, entry, &entryNotFoundError{Err: errors.New(\"bad checksum\")}\n\t}\n\treturn data, entry, nil\n}\n\n// OutputFile returns the name of the cache file storing output with the given OutputID.\nfunc (c *Cache) OutputFile(out OutputID) string {\n\tfile := c.fileName(out, \"d\")\n\tc.used(file)\n\treturn file\n}\n\n// Time constants for cache expiration.\n//\n// We set the mtime on a cache file on each use, but at most one per mtimeInterval (1 hour),\n// to avoid causing many unnecessary inode updates. The mtimes therefore\n// roughly reflect \"time of last use\" but may in fact be older by at most an hour.\n//\n// We scan the cache for entries to delete at most once per trimInterval (1 day).\n//\n// When we do scan the cache, we delete entries that have not been used for\n// at least trimLimit (5 days). Statistics gathered from a month of usage by\n// Go developers found that essentially all reuse of cached entries happened\n// within 5 days of the previous reuse. See golang.org/issue/22990.\nconst (\n\tmtimeInterval = 1 * time.Hour\n\ttrimInterval  = 24 * time.Hour\n\ttrimLimit     = 5 * 24 * time.Hour\n)\n\n// used makes a best-effort attempt to update mtime on file,\n// so that mtime reflects cache access time.\n//\n// Because the reflection only needs to be approximate,\n// and to reduce the amount of disk activity caused by using\n// cache entries, used only updates the mtime if the current\n// mtime is more than an hour old. This heuristic eliminates\n// nearly all of the mtime updates that would otherwise happen,\n// while still keeping the mtimes useful for cache trimming.\nfunc (c *Cache) used(file string) {\n\tinfo, err := os.Stat(file)\n\tif err == nil && c.now().Sub(info.ModTime()) < mtimeInterval {\n\t\treturn\n\t}\n\tos.Chtimes(file, c.now(), c.now())\n}\n\n// Trim removes old cache entries that are likely not to be reused.\nfunc (c *Cache) Trim() {\n\tnow := c.now()\n\n\t// We maintain in dir/trim.txt the time of the last completed cache trim.\n\t// If the cache has been trimmed recently enough, do nothing.\n\t// This is the common case.\n\tdata, _ := renameio.ReadFile(filepath.Join(c.dir, \"trim.txt\"))\n\tt, err := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64)\n\tif err == nil && now.Sub(time.Unix(t, 0)) < trimInterval {\n\t\treturn\n\t}\n\n\t// Trim each of the 256 subdirectories.\n\t// We subtract an additional mtimeInterval\n\t// to account for the imprecision of our \"last used\" mtimes.\n\tcutoff := now.Add(-trimLimit - mtimeInterval)\n\tfor i := range 256 {\n\t\tsubdir := filepath.Join(c.dir, fmt.Sprintf(\"%02x\", i))\n\t\tc.trimSubdir(subdir, cutoff)\n\t}\n\n\t// Ignore errors from here: if we don't write the complete timestamp, the\n\t// cache will appear older than it is, and we'll trim it again next time.\n\trenameio.WriteFile(filepath.Join(c.dir, \"trim.txt\"), fmt.Appendf(nil, \"%d\", now.Unix()), 0666)\n}\n\n// trimSubdir trims a single cache subdirectory.\nfunc (c *Cache) trimSubdir(subdir string, cutoff time.Time) {\n\t// Read all directory entries from subdir before removing\n\t// any files, in case removing files invalidates the file offset\n\t// in the directory scan. Also, ignore error from f.Readdirnames,\n\t// because we don't care about reporting the error and we still\n\t// want to process any entries found before the error.\n\tf, err := os.Open(subdir)\n\tif err != nil {\n\t\treturn\n\t}\n\tnames, _ := f.Readdirnames(-1)\n\tf.Close()\n\n\tfor _, name := range names {\n\t\t// Remove only cache entries (xxxx-a and xxxx-d).\n\t\tif !strings.HasSuffix(name, \"-a\") && !strings.HasSuffix(name, \"-d\") {\n\t\t\tcontinue\n\t\t}\n\t\tentry := filepath.Join(subdir, name)\n\t\tinfo, err := os.Stat(entry)\n\t\tif err == nil && info.ModTime().Before(cutoff) {\n\t\t\tos.Remove(entry)\n\t\t}\n\t}\n}\n\n// putIndexEntry adds an entry to the cache recording that executing the action\n// with the given id produces an output with the given output id (hash) and size.\nfunc (c *Cache) putIndexEntry(id ActionID, out OutputID, size int64, allowVerify bool) error {\n\t// Note: We expect that for one reason or another it may happen\n\t// that repeating an action produces a different output hash\n\t// (for example, if the output contains a time stamp or temp dir name).\n\t// While not ideal, this is also not a correctness problem, so we\n\t// don't make a big deal about it. In particular, we leave the action\n\t// cache entries writable specifically so that they can be overwritten.\n\t//\n\t// Setting GODEBUG=gocacheverify=1 does make a big deal:\n\t// in verify mode we are double-checking that the cache entries\n\t// are entirely reproducible. As just noted, this may be unrealistic\n\t// in some cases but the check is also useful for shaking out real bugs.\n\tentry := fmt.Sprintf(\"v1 %x %x %20d %20d\\n\", id, out, size, time.Now().UnixNano())\n\tif verify && allowVerify {\n\t\told, err := c.get(id)\n\t\tif err == nil && (old.OutputID != out || old.Size != size) {\n\t\t\t// panic to show stack trace, so we can see what code is generating this cache entry.\n\t\t\tmsg := fmt.Sprintf(\"go: internal cache error: cache verify failed: id=%x changed:<<<\\n%s\\n>>>\\nold: %x %d\\nnew: %x %d\", id, reverseHash(id), out, size, old.OutputID, old.Size)\n\t\t\tpanic(msg)\n\t\t}\n\t}\n\tfile := c.fileName(id, \"a\")\n\n\t// Copy file to cache directory.\n\tmode := os.O_WRONLY | os.O_CREATE\n\tf, err := os.OpenFile(file, mode, 0666)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = f.WriteString(entry)\n\tif err == nil {\n\t\t// Truncate the file only *after* writing it.\n\t\t// (This should be a no-op, but truncate just in case of previous corruption.)\n\t\t//\n\t\t// This differs from ioutil.WriteFile, which truncates to 0 *before* writing\n\t\t// via os.O_TRUNC. Truncating only after writing ensures that a second write\n\t\t// of the same content to the same file is idempotent, and does not — even\n\t\t// temporarily! — undo the effect of the first write.\n\t\terr = f.Truncate(int64(len(entry)))\n\t}\n\tif closeErr := f.Close(); err == nil {\n\t\terr = closeErr\n\t}\n\tif err != nil {\n\t\t// TODO(bcmills): This Remove potentially races with another go command writing to file.\n\t\t// Can we eliminate it?\n\t\tos.Remove(file)\n\t\treturn err\n\t}\n\tos.Chtimes(file, c.now(), c.now()) // mainly for tests\n\n\treturn nil\n}\n\n// Put stores the given output in the cache as the output for the action ID.\n// It may read file twice. The content of file must not change between the two passes.\nfunc (c *Cache) Put(id ActionID, file io.ReadSeeker) (OutputID, int64, error) {\n\treturn c.put(id, file, true)\n}\n\n// PutNoVerify is like Put but disables the verify check\n// when GODEBUG=goverifycache=1 is set.\n// It is meant for data that is OK to cache but that we expect to vary slightly from run to run,\n// like test output containing times and the like.\nfunc (c *Cache) PutNoVerify(id ActionID, file io.ReadSeeker) (OutputID, int64, error) {\n\treturn c.put(id, file, false)\n}\n\nfunc (c *Cache) put(id ActionID, file io.ReadSeeker, allowVerify bool) (OutputID, int64, error) {\n\t// Compute output ID.\n\th := sha256.New()\n\tif _, err := file.Seek(0, 0); err != nil {\n\t\treturn OutputID{}, 0, err\n\t}\n\tsize, err := io.Copy(h, file)\n\tif err != nil {\n\t\treturn OutputID{}, 0, err\n\t}\n\tvar out OutputID\n\th.Sum(out[:0])\n\n\t// Copy to cached output file (if not already present).\n\tif err := c.copyFile(file, out, size); err != nil {\n\t\treturn out, size, err\n\t}\n\n\t// Add to cache index.\n\treturn out, size, c.putIndexEntry(id, out, size, allowVerify)\n}\n\n// PutBytes stores the given bytes in the cache as the output for the action ID.\nfunc (c *Cache) PutBytes(id ActionID, data []byte) error {\n\t_, _, err := c.Put(id, bytes.NewReader(data))\n\treturn err\n}\n\n// copyFile copies file into the cache, expecting it to have the given\n// output ID and size, if that file is not present already.\nfunc (c *Cache) copyFile(file io.ReadSeeker, out OutputID, size int64) error {\n\tname := c.fileName(out, \"d\")\n\tinfo, err := os.Stat(name)\n\tif err == nil && info.Size() == size {\n\t\t// Check hash.\n\t\tif f, err := os.Open(name); err == nil {\n\t\t\th := sha256.New()\n\t\t\tio.Copy(h, f)\n\t\t\tf.Close()\n\t\t\tvar out2 OutputID\n\t\t\th.Sum(out2[:0])\n\t\t\tif out == out2 {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\t// Hash did not match. Fall through and rewrite file.\n\t}\n\n\t// Copy file to cache directory.\n\tmode := os.O_RDWR | os.O_CREATE\n\tif err == nil && info.Size() > size { // shouldn't happen but fix in case\n\t\tmode |= os.O_TRUNC\n\t}\n\tf, err := os.OpenFile(name, mode, 0666)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\tif size == 0 {\n\t\t// File now exists with correct size.\n\t\t// Only one possible zero-length file, so contents are OK too.\n\t\t// Early return here makes sure there's a \"last byte\" for code below.\n\t\treturn nil\n\t}\n\n\t// From here on, if any of the I/O writing the file fails,\n\t// we make a best-effort attempt to truncate the file f\n\t// before returning, to avoid leaving bad bytes in the file.\n\n\t// Copy file to f, but also into h to double-check hash.\n\tif _, err := file.Seek(0, 0); err != nil {\n\t\tf.Truncate(0)\n\t\treturn err\n\t}\n\th := sha256.New()\n\tw := io.MultiWriter(f, h)\n\tif _, err := io.CopyN(w, file, size-1); err != nil {\n\t\tf.Truncate(0)\n\t\treturn err\n\t}\n\t// Check last byte before writing it; writing it will make the size match\n\t// what other processes expect to find and might cause them to start\n\t// using the file.\n\tbuf := make([]byte, 1)\n\tif _, err := file.Read(buf); err != nil {\n\t\tf.Truncate(0)\n\t\treturn err\n\t}\n\th.Write(buf)\n\tsum := h.Sum(nil)\n\tif !bytes.Equal(sum, out[:]) {\n\t\tf.Truncate(0)\n\t\treturn fmt.Errorf(\"file content changed underfoot\")\n\t}\n\n\t// Commit cache file entry.\n\tif _, err := f.Write(buf); err != nil {\n\t\tf.Truncate(0)\n\t\treturn err\n\t}\n\tif err := f.Close(); err != nil {\n\t\t// Data might not have been written,\n\t\t// but file may look like it is the right size.\n\t\t// To be extra careful, remove cached file.\n\t\tos.Remove(name)\n\t\treturn err\n\t}\n\tos.Chtimes(name, c.now(), c.now()) // mainly for tests\n\n\treturn nil\n}\n"
  },
  {
    "path": "lintcmd/cache/cache_test.go",
    "content": "// Copyright 2017 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage cache\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc init() {\n\tverify = false // even if GODEBUG is set\n}\n\nfunc TestBasic(t *testing.T) {\n\tdir := t.TempDir()\n\t_, err := Open(filepath.Join(dir, \"notexist\"))\n\tif err == nil {\n\t\tt.Fatal(`Open(\"tmp/notexist\") succeeded, want failure`)\n\t}\n\n\tcdir := filepath.Join(dir, \"c1\")\n\tif err := os.Mkdir(cdir, 0777); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tc1, err := Open(cdir)\n\tif err != nil {\n\t\tt.Fatalf(\"Open(c1) (create): %v\", err)\n\t}\n\tif err := c1.putIndexEntry(dummyID(1), dummyID(12), 13, true); err != nil {\n\t\tt.Fatalf(\"addIndexEntry: %v\", err)\n\t}\n\tif err := c1.putIndexEntry(dummyID(1), dummyID(2), 3, true); err != nil { // overwrite entry\n\t\tt.Fatalf(\"addIndexEntry: %v\", err)\n\t}\n\tif entry, err := c1.Get(dummyID(1)); err != nil || entry.OutputID != dummyID(2) || entry.Size != 3 {\n\t\tt.Fatalf(\"c1.Get(1) = %x, %v, %v, want %x, %v, nil\", entry.OutputID, entry.Size, err, dummyID(2), 3)\n\t}\n\n\tc2, err := Open(cdir)\n\tif err != nil {\n\t\tt.Fatalf(\"Open(c2) (reuse): %v\", err)\n\t}\n\tif entry, err := c2.Get(dummyID(1)); err != nil || entry.OutputID != dummyID(2) || entry.Size != 3 {\n\t\tt.Fatalf(\"c2.Get(1) = %x, %v, %v, want %x, %v, nil\", entry.OutputID, entry.Size, err, dummyID(2), 3)\n\t}\n\tif err := c2.putIndexEntry(dummyID(2), dummyID(3), 4, true); err != nil {\n\t\tt.Fatalf(\"addIndexEntry: %v\", err)\n\t}\n\tif entry, err := c1.Get(dummyID(2)); err != nil || entry.OutputID != dummyID(3) || entry.Size != 4 {\n\t\tt.Fatalf(\"c1.Get(2) = %x, %v, %v, want %x, %v, nil\", entry.OutputID, entry.Size, err, dummyID(3), 4)\n\t}\n}\n\nfunc TestGrowth(t *testing.T) {\n\tdir := t.TempDir()\n\n\tc, err := Open(dir)\n\tif err != nil {\n\t\tt.Fatalf(\"Open: %v\", err)\n\t}\n\n\tn := 10000\n\tif testing.Short() {\n\t\tn = 10\n\t}\n\n\tfor i := 0; i < n; i++ {\n\t\tif err := c.putIndexEntry(dummyID(i), dummyID(i*99), int64(i)*101, true); err != nil {\n\t\t\tt.Fatalf(\"addIndexEntry: %v\", err)\n\t\t}\n\t\tid := ActionID(dummyID(i))\n\t\tentry, err := c.Get(id)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Get(%x): %v\", id, err)\n\t\t}\n\t\tif entry.OutputID != dummyID(i*99) || entry.Size != int64(i)*101 {\n\t\t\tt.Errorf(\"Get(%x) = %x, %d, want %x, %d\", id, entry.OutputID, entry.Size, dummyID(i*99), int64(i)*101)\n\t\t}\n\t}\n\tfor i := 0; i < n; i++ {\n\t\tid := ActionID(dummyID(i))\n\t\tentry, err := c.Get(id)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Get2(%x): %v\", id, err)\n\t\t}\n\t\tif entry.OutputID != dummyID(i*99) || entry.Size != int64(i)*101 {\n\t\t\tt.Errorf(\"Get2(%x) = %x, %d, want %x, %d\", id, entry.OutputID, entry.Size, dummyID(i*99), int64(i)*101)\n\t\t}\n\t}\n}\n\nfunc TestVerifyPanic(t *testing.T) {\n\tos.Setenv(\"GODEBUG\", \"gocacheverify=1\")\n\tinitEnv()\n\tdefer func() {\n\t\tos.Unsetenv(\"GODEBUG\")\n\t\tverify = false\n\t}()\n\n\tif !verify {\n\t\tt.Fatal(\"initEnv did not set verify\")\n\t}\n\n\tdir := t.TempDir()\n\n\tc, err := Open(dir)\n\tif err != nil {\n\t\tt.Fatalf(\"Open: %v\", err)\n\t}\n\n\tid := ActionID(dummyID(1))\n\tif err := c.PutBytes(id, []byte(\"abc\")); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tdefer func() {\n\t\tif err := recover(); err != nil {\n\t\t\tt.Log(err)\n\t\t\treturn\n\t\t}\n\t}()\n\tc.PutBytes(id, []byte(\"def\"))\n\tt.Fatal(\"mismatched Put did not panic in verify mode\")\n}\n\nfunc dummyID(x int) [HashSize]byte {\n\tvar out [HashSize]byte\n\tbinary.LittleEndian.PutUint64(out[:], uint64(x))\n\treturn out\n}\n\nfunc TestCacheTrim(t *testing.T) {\n\tdir := t.TempDir()\n\n\tc, err := Open(dir)\n\tif err != nil {\n\t\tt.Fatalf(\"Open: %v\", err)\n\t}\n\tconst start = 1000000000\n\tnow := int64(start)\n\tc.now = func() time.Time { return time.Unix(now, 0) }\n\n\tcheckTime := func(name string, mtime int64) {\n\t\tt.Helper()\n\t\tfile := filepath.Join(c.dir, name[:2], name)\n\t\tinfo, err := os.Stat(file)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif info.ModTime().Unix() != mtime {\n\t\t\tt.Fatalf(\"%s mtime = %d, want %d\", name, info.ModTime().Unix(), mtime)\n\t\t}\n\t}\n\n\tid := ActionID(dummyID(1))\n\tc.PutBytes(id, []byte(\"abc\"))\n\tentry, _ := c.Get(id)\n\tc.PutBytes(ActionID(dummyID(2)), []byte(\"def\"))\n\tmtime := now\n\tcheckTime(fmt.Sprintf(\"%x-a\", id), mtime)\n\tcheckTime(fmt.Sprintf(\"%x-d\", entry.OutputID), mtime)\n\n\t// Get should not change recent mtimes.\n\tnow = start + 10\n\tc.Get(id)\n\tcheckTime(fmt.Sprintf(\"%x-a\", id), mtime)\n\tcheckTime(fmt.Sprintf(\"%x-d\", entry.OutputID), mtime)\n\n\t// Get should change distant mtimes.\n\tnow = start + 5000\n\tmtime2 := now\n\tif _, err := c.Get(id); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tc.OutputFile(entry.OutputID)\n\tcheckTime(fmt.Sprintf(\"%x-a\", id), mtime2)\n\tcheckTime(fmt.Sprintf(\"%x-d\", entry.OutputID), mtime2)\n\n\t// Trim should leave everything alone: it's all too new.\n\tc.Trim()\n\tif _, err := c.Get(id); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tc.OutputFile(entry.OutputID)\n\tdata, err := os.ReadFile(filepath.Join(dir, \"trim.txt\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tcheckTime(fmt.Sprintf(\"%x-a\", dummyID(2)), start)\n\n\t// Trim less than a day later should not do any work at all.\n\tnow = start + 80000\n\tc.Trim()\n\tif _, err := c.Get(id); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tc.OutputFile(entry.OutputID)\n\tdata2, err := os.ReadFile(filepath.Join(dir, \"trim.txt\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !bytes.Equal(data, data2) {\n\t\tt.Fatalf(\"second trim did work: %q -> %q\", data, data2)\n\t}\n\n\t// Fast forward and do another trim just before the 5 day cutoff.\n\t// Note that because of usedQuantum the cutoff is actually 5 days + 1 hour.\n\t// We used c.Get(id) just now, so 5 days later it should still be kept.\n\t// On the other hand almost a full day has gone by since we wrote dummyID(2)\n\t// and we haven't looked at it since, so 5 days later it should be gone.\n\tnow += 5 * 86400\n\tcheckTime(fmt.Sprintf(\"%x-a\", dummyID(2)), start)\n\tc.Trim()\n\tif _, err := c.Get(id); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tc.OutputFile(entry.OutputID)\n\tmtime3 := now\n\tif _, err := c.Get(dummyID(2)); err == nil { // haven't done a Get for this since original write above\n\t\tt.Fatalf(\"Trim did not remove dummyID(2)\")\n\t}\n\n\t// The c.Get(id) refreshed id's mtime again.\n\t// Check that another 5 days later it is still not gone,\n\t// but check by using checkTime, which doesn't bring mtime forward.\n\tnow += 5 * 86400\n\tc.Trim()\n\tcheckTime(fmt.Sprintf(\"%x-a\", id), mtime3)\n\tcheckTime(fmt.Sprintf(\"%x-d\", entry.OutputID), mtime3)\n\n\t// Half a day later Trim should still be a no-op, because there was a Trim recently.\n\t// Even though the entry for id is now old enough to be trimmed,\n\t// it gets a reprieve until the time comes for a new Trim scan.\n\tnow += 86400 / 2\n\tc.Trim()\n\tcheckTime(fmt.Sprintf(\"%x-a\", id), mtime3)\n\tcheckTime(fmt.Sprintf(\"%x-d\", entry.OutputID), mtime3)\n\n\t// Another half a day later, Trim should actually run, and it should remove id.\n\tnow += 86400/2 + 1\n\tc.Trim()\n\tif _, err := c.Get(dummyID(1)); err == nil {\n\t\tt.Fatal(\"Trim did not remove dummyID(1)\")\n\t}\n}\n"
  },
  {
    "path": "lintcmd/cache/default.go",
    "content": "// Copyright 2017 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage cache\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync\"\n)\n\n// Default returns the default cache to use.\nfunc Default() (*Cache, error) {\n\tdefaultOnce.Do(initDefaultCache)\n\treturn defaultCache, defaultDirErr\n}\n\nvar (\n\tdefaultOnce  sync.Once\n\tdefaultCache *Cache\n)\n\n// cacheREADME is a message stored in a README in the cache directory.\n// Because the cache lives outside the normal Go trees, we leave the\n// README as a courtesy to explain where it came from.\nconst cacheREADME = `This directory holds cached build artifacts from staticcheck.\n`\n\n// initDefaultCache does the work of finding the default cache\n// the first time Default is called.\nfunc initDefaultCache() {\n\tdir := DefaultDir()\n\tif err := os.MkdirAll(dir, 0777); err != nil {\n\t\tlog.Fatalf(\"failed to initialize build cache at %s: %s\\n\", dir, err)\n\t}\n\tif _, err := os.Stat(filepath.Join(dir, \"README\")); err != nil {\n\t\t// Best effort.\n\t\tos.WriteFile(filepath.Join(dir, \"README\"), []byte(cacheREADME), 0666)\n\t}\n\n\tc, err := Open(dir)\n\tif err != nil {\n\t\tlog.Fatalf(\"failed to initialize build cache at %s: %s\\n\", dir, err)\n\t}\n\tdefaultCache = c\n}\n\nvar (\n\tdefaultDirOnce sync.Once\n\tdefaultDir     string\n\tdefaultDirErr  error\n)\n\n// DefaultDir returns the effective STATICCHECK_CACHE setting.\nfunc DefaultDir() string {\n\t// Save the result of the first call to DefaultDir for later use in\n\t// initDefaultCache. cmd/go/main.go explicitly sets GOCACHE so that\n\t// subprocesses will inherit it, but that means initDefaultCache can't\n\t// otherwise distinguish between an explicit \"off\" and a UserCacheDir error.\n\n\tdefaultDirOnce.Do(func() {\n\t\tdefaultDir = os.Getenv(\"STATICCHECK_CACHE\")\n\t\tif filepath.IsAbs(defaultDir) {\n\t\t\treturn\n\t\t}\n\t\tif defaultDir != \"\" {\n\t\t\tdefaultDirErr = fmt.Errorf(\"STATICCHECK_CACHE is not an absolute path\")\n\t\t\treturn\n\t\t}\n\n\t\t// Compute default location.\n\t\tdir, err := os.UserCacheDir()\n\t\tif err != nil {\n\t\t\tdefaultDirErr = fmt.Errorf(\"STATICCHECK_CACHE is not defined and %v\", err)\n\t\t\treturn\n\t\t}\n\t\tdefaultDir = filepath.Join(dir, \"staticcheck\")\n\t})\n\n\treturn defaultDir\n}\n"
  },
  {
    "path": "lintcmd/cache/hash.go",
    "content": "// Copyright 2017 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage cache\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"fmt\"\n\t\"hash\"\n\t\"io\"\n\t\"os\"\n\t\"sync\"\n)\n\nvar debugHash = false // set when GODEBUG=gocachehash=1\n\n// HashSize is the number of bytes in a hash.\nconst HashSize = 32\n\n// A Hash provides access to the canonical hash function used to index the cache.\n// The current implementation uses salted SHA256, but clients must not assume this.\ntype Hash struct {\n\th    hash.Hash\n\tname string        // for debugging\n\tbuf  *bytes.Buffer // for verify\n}\n\n// Subkey returns an action ID corresponding to mixing a parent\n// action ID with a string description of the subkey.\nfunc Subkey(parent ActionID, desc string) ActionID {\n\th := sha256.New()\n\th.Write([]byte(\"subkey:\"))\n\th.Write(parent[:])\n\th.Write([]byte(desc))\n\tvar out ActionID\n\th.Sum(out[:0])\n\tif debugHash {\n\t\tfmt.Fprintf(os.Stderr, \"HASH subkey %x %q = %x\\n\", parent, desc, out)\n\t}\n\tif verify {\n\t\thashDebug.Lock()\n\t\thashDebug.m[out] = fmt.Sprintf(\"subkey %x %q\", parent, desc)\n\t\thashDebug.Unlock()\n\t}\n\treturn out\n}\n\n// NewHash returns a new Hash.\n// The caller is expected to Write data to it and then call Sum.\nfunc (c *Cache) NewHash(name string) *Hash {\n\th := &Hash{h: sha256.New(), name: name}\n\tif debugHash {\n\t\tfmt.Fprintf(os.Stderr, \"HASH[%s]\\n\", h.name)\n\t}\n\th.Write(c.salt)\n\tif verify {\n\t\th.buf = new(bytes.Buffer)\n\t}\n\treturn h\n}\n\n// Write writes data to the running hash.\nfunc (h *Hash) Write(b []byte) (int, error) {\n\tif debugHash {\n\t\tfmt.Fprintf(os.Stderr, \"HASH[%s]: %q\\n\", h.name, b)\n\t}\n\tif h.buf != nil {\n\t\th.buf.Write(b)\n\t}\n\treturn h.h.Write(b)\n}\n\n// Sum returns the hash of the data written previously.\nfunc (h *Hash) Sum() [HashSize]byte {\n\tvar out [HashSize]byte\n\th.h.Sum(out[:0])\n\tif debugHash {\n\t\tfmt.Fprintf(os.Stderr, \"HASH[%s]: %x\\n\", h.name, out)\n\t}\n\tif h.buf != nil {\n\t\thashDebug.Lock()\n\t\tif hashDebug.m == nil {\n\t\t\thashDebug.m = make(map[[HashSize]byte]string)\n\t\t}\n\t\thashDebug.m[out] = h.buf.String()\n\t\thashDebug.Unlock()\n\t}\n\treturn out\n}\n\n// In GODEBUG=gocacheverify=1 mode,\n// hashDebug holds the input to every computed hash ID,\n// so that we can work backward from the ID involved in a\n// cache entry mismatch to a description of what should be there.\nvar hashDebug struct {\n\tsync.Mutex\n\tm map[[HashSize]byte]string\n}\n\n// reverseHash returns the input used to compute the hash id.\nfunc reverseHash(id [HashSize]byte) string {\n\thashDebug.Lock()\n\ts := hashDebug.m[id]\n\thashDebug.Unlock()\n\treturn s\n}\n\nvar hashFileCache struct {\n\tsync.Mutex\n\tm map[string][HashSize]byte\n}\n\n// FileHash returns the hash of the named file.\n// It caches repeated lookups for a given file,\n// and the cache entry for a file can be initialized\n// using SetFileHash.\n// The hash used by FileHash is not the same as\n// the hash used by NewHash.\nfunc FileHash(file string) ([HashSize]byte, error) {\n\thashFileCache.Lock()\n\tout, ok := hashFileCache.m[file]\n\thashFileCache.Unlock()\n\n\tif ok {\n\t\treturn out, nil\n\t}\n\n\th := sha256.New()\n\tf, err := os.Open(file)\n\tif err != nil {\n\t\tif debugHash {\n\t\t\tfmt.Fprintf(os.Stderr, \"HASH %s: %v\\n\", file, err)\n\t\t}\n\t\treturn [HashSize]byte{}, err\n\t}\n\t_, err = io.Copy(h, f)\n\tf.Close()\n\tif err != nil {\n\t\tif debugHash {\n\t\t\tfmt.Fprintf(os.Stderr, \"HASH %s: %v\\n\", file, err)\n\t\t}\n\t\treturn [HashSize]byte{}, err\n\t}\n\th.Sum(out[:0])\n\tif debugHash {\n\t\tfmt.Fprintf(os.Stderr, \"HASH %s: %x\\n\", file, out)\n\t}\n\n\tSetFileHash(file, out)\n\treturn out, nil\n}\n\n// SetFileHash sets the hash returned by FileHash for file.\nfunc SetFileHash(file string, sum [HashSize]byte) {\n\thashFileCache.Lock()\n\tif hashFileCache.m == nil {\n\t\thashFileCache.m = make(map[string][HashSize]byte)\n\t}\n\thashFileCache.m[file] = sum\n\thashFileCache.Unlock()\n}\n"
  },
  {
    "path": "lintcmd/cache/hash_test.go",
    "content": "// Copyright 2017 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage cache\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestHash(t *testing.T) {\n\tc := &Cache{}\n\th := c.NewHash(\"alice\")\n\th.Write([]byte(\"hello world\"))\n\tsum := fmt.Sprintf(\"%x\", h.Sum())\n\twant := \"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9\"\n\tif sum != want {\n\t\tt.Errorf(\"hash(hello world) = %v, want %v\", sum, want)\n\t}\n}\n\nfunc TestHashFile(t *testing.T) {\n\tf, err := os.CreateTemp(\"\", \"cmd-go-test-\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tname := f.Name()\n\tfmt.Fprintf(f, \"hello world\")\n\tdefer os.Remove(name)\n\tif err := f.Close(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar h ActionID // make sure hash result is assignable to ActionID\n\th, err = FileHash(name)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tsum := fmt.Sprintf(\"%x\", h)\n\twant := \"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9\"\n\tif sum != want {\n\t\tt.Errorf(\"hash(hello world) = %v, want %v\", sum, want)\n\t}\n}\n"
  },
  {
    "path": "lintcmd/cmd.go",
    "content": "// Package lintcmd implements the frontend of an analysis runner.\n// It serves as the entry-point for the staticcheck command, and can also be used to implement custom linters that behave like staticcheck.\npackage lintcmd\n\nimport (\n\t\"bufio\"\n\t\"encoding/gob\"\n\t\"flag\"\n\t\"fmt\"\n\t\"go/token\"\n\tstdversion \"go/version\"\n\t\"io\"\n\t\"log\"\n\t\"maps\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"runtime/pprof\"\n\t\"runtime/trace\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/config\"\n\t\"honnef.co/go/tools/go/loader\"\n\t\"honnef.co/go/tools/lintcmd/version\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/buildutil\"\n)\n\ntype buildConfig struct {\n\tName  string\n\tEnvs  []string\n\tFlags []string\n}\n\n// Command represents a linter command line tool.\ntype Command struct {\n\tname           string\n\tanalyzers      map[string]*lint.Analyzer\n\tversion        string\n\tmachineVersion string\n\n\tflags struct {\n\t\tfs *flag.FlagSet\n\n\t\ttags        string\n\t\ttests       bool\n\t\tshowIgnored bool\n\t\tformatter   string\n\n\t\t// mutually exclusive mode flags\n\t\texplain      string\n\t\tprintVersion bool\n\t\tlistChecks   bool\n\t\tmerge        bool\n\n\t\tmatrix bool\n\n\t\tdebugCpuprofile       string\n\t\tdebugMemprofile       string\n\t\tdebugVersion          bool\n\t\tdebugNoCompileErrors  bool\n\t\tdebugMeasureAnalyzers string\n\t\tdebugTrace            string\n\n\t\tchecks    list\n\t\tfail      list\n\t\tgoVersion versionFlag\n\t}\n}\n\n// NewCommand returns a new Command.\nfunc NewCommand(name string) *Command {\n\tcmd := &Command{\n\t\tname:           name,\n\t\tanalyzers:      map[string]*lint.Analyzer{},\n\t\tversion:        \"devel\",\n\t\tmachineVersion: \"devel\",\n\t}\n\tcmd.initFlagSet(name)\n\treturn cmd\n}\n\n// SetVersion sets the command's version.\n// It is divided into a human part and a machine part.\n// For example, Staticcheck 2020.2.1 had the human version \"2020.2.1\" and the machine version \"v0.1.1\".\n// If you only use Semver, you can set both parts to the same value.\n//\n// Calling this method is optional. Both versions default to \"devel\", and we'll attempt to deduce more version information from the Go module.\nfunc (cmd *Command) SetVersion(human, machine string) {\n\tcmd.version = human\n\tcmd.machineVersion = machine\n}\n\n// FlagSet returns the command's flag set.\n// This can be used to add additional command line arguments.\nfunc (cmd *Command) FlagSet() *flag.FlagSet {\n\treturn cmd.flags.fs\n}\n\n// AddAnalyzers adds analyzers to the command.\n// These are lint.Analyzer analyzers, which wrap analysis.Analyzer analyzers, bundling them with structured documentation.\n//\n// To add analysis.Analyzer analyzers without providing structured documentation, use AddBareAnalyzers.\nfunc (cmd *Command) AddAnalyzers(as ...*lint.Analyzer) {\n\tfor _, a := range as {\n\t\tcmd.analyzers[a.Analyzer.Name] = a\n\t}\n}\n\n// AddBareAnalyzers adds bare analyzers to the command.\nfunc (cmd *Command) AddBareAnalyzers(as ...*analysis.Analyzer) {\n\tfor _, a := range as {\n\t\tvar title, text string\n\t\tif idx := strings.Index(a.Doc, \"\\n\\n\"); idx > -1 {\n\t\t\ttitle = a.Doc[:idx]\n\t\t\ttext = a.Doc[idx+2:]\n\t\t}\n\n\t\tdoc := &lint.RawDocumentation{\n\t\t\tTitle:    title,\n\t\t\tText:     text,\n\t\t\tSeverity: lint.SeverityWarning,\n\t\t}\n\n\t\tcmd.analyzers[a.Name] = &lint.Analyzer{\n\t\t\tDoc:      doc,\n\t\t\tAnalyzer: a,\n\t\t}\n\t}\n}\n\nfunc (cmd *Command) initFlagSet(name string) {\n\tflags := flag.NewFlagSet(\"\", flag.ExitOnError)\n\tcmd.flags.fs = flags\n\tflags.Usage = usage(name, flags)\n\n\tflags.StringVar(&cmd.flags.tags, \"tags\", \"\", \"List of `build tags`\")\n\tflags.BoolVar(&cmd.flags.tests, \"tests\", true, \"Include tests\")\n\tflags.BoolVar(&cmd.flags.printVersion, \"version\", false, \"Print version and exit\")\n\tflags.BoolVar(&cmd.flags.showIgnored, \"show-ignored\", false, \"Don't filter ignored diagnostics\")\n\tflags.StringVar(&cmd.flags.formatter, \"f\", \"text\", \"Output `format` (valid choices are 'stylish', 'text' and 'json')\")\n\tflags.StringVar(&cmd.flags.explain, \"explain\", \"\", \"Print description of `check`\")\n\tflags.BoolVar(&cmd.flags.listChecks, \"list-checks\", false, \"List all available checks\")\n\tflags.BoolVar(&cmd.flags.merge, \"merge\", false, \"Merge results of multiple Staticcheck runs\")\n\tflags.BoolVar(&cmd.flags.matrix, \"matrix\", false, \"Read a build config matrix from stdin\")\n\n\tflags.StringVar(&cmd.flags.debugCpuprofile, \"debug.cpuprofile\", \"\", \"Write CPU profile to `file`\")\n\tflags.StringVar(&cmd.flags.debugMemprofile, \"debug.memprofile\", \"\", \"Write memory profile to `file`\")\n\tflags.BoolVar(&cmd.flags.debugVersion, \"debug.version\", false, \"Print detailed version information about this program\")\n\tflags.BoolVar(&cmd.flags.debugNoCompileErrors, \"debug.no-compile-errors\", false, \"Don't print compile errors\")\n\tflags.StringVar(&cmd.flags.debugMeasureAnalyzers, \"debug.measure-analyzers\", \"\", \"Write analysis measurements to `file`. `file` will be opened for appending if it already exists.\")\n\tflags.StringVar(&cmd.flags.debugTrace, \"debug.trace\", \"\", \"Write trace to `file`\")\n\n\tcmd.flags.checks = list{\"inherit\"}\n\tcmd.flags.fail = list{\"all\"}\n\tcmd.flags.goVersion = versionFlag(\"module\")\n\tflags.Var(&cmd.flags.checks, \"checks\", \"Comma-separated list of `checks` to enable.\")\n\tflags.Var(&cmd.flags.fail, \"fail\", \"Comma-separated list of `checks` that can cause a non-zero exit status.\")\n\tflags.Var(&cmd.flags.goVersion, \"go\", \"Target Go `version` in the format '1.x', or the literal 'module' to use the module's Go version\")\n}\n\ntype list []string\n\nfunc (list *list) String() string {\n\treturn `\"` + strings.Join(*list, \",\") + `\"`\n}\n\nfunc (list *list) Set(s string) error {\n\tif s == \"\" {\n\t\t*list = nil\n\t\treturn nil\n\t}\n\n\telems := strings.Split(s, \",\")\n\tfor i, elem := range elems {\n\t\telems[i] = strings.TrimSpace(elem)\n\t}\n\t*list = elems\n\treturn nil\n}\n\ntype versionFlag string\n\nfunc (v *versionFlag) String() string {\n\treturn fmt.Sprintf(\"%q\", string(*v))\n}\n\nfunc (v *versionFlag) Set(s string) error {\n\tif s == \"module\" {\n\t\t*v = \"module\"\n\t} else {\n\t\torig := s\n\t\tif !strings.HasPrefix(s, \"go\") {\n\t\t\ts = \"go\" + s\n\t\t}\n\t\tif stdversion.IsValid(s) {\n\t\t\t*v = versionFlag(s)\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"%q is not a valid Go version\", orig)\n\t\t}\n\t}\n\treturn nil\n}\n\n// ParseFlags parses command line flags.\n// It must be called before calling Run.\n// After calling ParseFlags, the values of flags can be accessed.\n//\n// Example:\n//\n//\tcmd.ParseFlags(os.Args[1:])\nfunc (cmd *Command) ParseFlags(args []string) {\n\tcmd.flags.fs.Parse(args)\n}\n\n// diagnosticDescriptor represents the uniquely identifying information of diagnostics.\ntype diagnosticDescriptor struct {\n\tPosition token.Position\n\tEnd      token.Position\n\tCategory string\n\tMessage  string\n}\n\nfunc (diag diagnostic) descriptor() diagnosticDescriptor {\n\treturn diagnosticDescriptor{\n\t\tPosition: diag.Position,\n\t\tEnd:      diag.End,\n\t\tCategory: diag.Category,\n\t\tMessage:  diag.Message,\n\t}\n}\n\ntype run struct {\n\tcheckedFiles map[string]struct{}\n\tdiagnostics  map[diagnosticDescriptor]diagnostic\n}\n\nfunc runFromLintResult(res lintResult) run {\n\tout := run{\n\t\tcheckedFiles: map[string]struct{}{},\n\t\tdiagnostics:  map[diagnosticDescriptor]diagnostic{},\n\t}\n\n\tfor _, cf := range res.CheckedFiles {\n\t\tout.checkedFiles[cf] = struct{}{}\n\t}\n\tfor _, diag := range res.Diagnostics {\n\t\tout.diagnostics[diag.descriptor()] = diag\n\t}\n\treturn out\n}\n\nfunc decodeGob(br io.ByteReader) ([]run, error) {\n\tvar runs []run\n\tfor {\n\t\tvar res lintResult\n\t\tif err := gob.NewDecoder(br.(io.Reader)).Decode(&res); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t} else {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\truns = append(runs, runFromLintResult(res))\n\t}\n\treturn runs, nil\n}\n\n// Execute runs all registered analyzers and reports their findings.\n// The status code returned can be used for os.Exit(cmd.Execute()).\nfunc (cmd *Command) Execute() int {\n\t// Set up profiling and tracing\n\tif path := cmd.flags.debugCpuprofile; path != \"\" {\n\t\tf, err := os.Create(path)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tpprof.StartCPUProfile(f)\n\t}\n\tif path := cmd.flags.debugTrace; path != \"\" {\n\t\tf, err := os.Create(path)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\ttrace.Start(f)\n\t}\n\n\t// Update the default config's list of enabled checks\n\tdefaultChecks := []string{\"all\"}\n\tfor _, a := range cmd.analyzers {\n\t\tif a.Doc.NonDefault {\n\t\t\tdefaultChecks = append(defaultChecks, \"-\"+a.Analyzer.Name)\n\t\t}\n\t}\n\tconfig.DefaultConfig.Checks = defaultChecks\n\n\t// Run the appropriate mode\n\tvar exit int\n\tswitch {\n\tcase cmd.flags.debugVersion:\n\t\texit = cmd.printDebugVersion()\n\tcase cmd.flags.listChecks:\n\t\texit = cmd.listChecks()\n\tcase cmd.flags.printVersion:\n\t\texit = cmd.printVersion()\n\tcase cmd.flags.explain != \"\":\n\t\texit = cmd.explain()\n\tcase cmd.flags.merge:\n\t\texit = cmd.merge()\n\tdefault:\n\t\texit = cmd.lint()\n\t}\n\n\t// Stop profiling\n\tif cmd.flags.debugCpuprofile != \"\" {\n\t\tpprof.StopCPUProfile()\n\t}\n\tif path := cmd.flags.debugMemprofile; path != \"\" {\n\t\tf, err := os.Create(path)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\truntime.GC()\n\t\tpprof.WriteHeapProfile(f)\n\t}\n\tif cmd.flags.debugTrace != \"\" {\n\t\ttrace.Stop()\n\t}\n\n\treturn exit\n}\n\n// Run runs all registered analyzers and reports their findings.\n// It always calls os.Exit and does not return.\nfunc (cmd *Command) Run() {\n\tos.Exit(cmd.Execute())\n}\n\nfunc (cmd *Command) printDebugVersion() int {\n\tversion.Verbose(cmd.version, cmd.machineVersion)\n\treturn 0\n}\n\nfunc (cmd *Command) listChecks() int {\n\tcs := slices.Collect(maps.Values(cmd.analyzers))\n\tsort.Slice(cs, func(i, j int) bool {\n\t\treturn cs[i].Analyzer.Name < cs[j].Analyzer.Name\n\t})\n\tfor _, c := range cs {\n\t\tvar title string\n\t\tif c.Doc != nil {\n\t\t\ttitle = c.Doc.Compile().Title\n\t\t}\n\t\tfmt.Printf(\"%s %s\\n\", c.Analyzer.Name, title)\n\t}\n\treturn 0\n}\n\nfunc (cmd *Command) printVersion() int {\n\tversion.Print(cmd.version, cmd.machineVersion)\n\treturn 0\n}\n\nfunc (cmd *Command) explain() int {\n\texplain := cmd.flags.explain\n\tcheck, ok := cmd.analyzers[explain]\n\tif !ok {\n\t\tfmt.Fprintln(os.Stderr, \"Couldn't find check\", explain)\n\t\treturn 1\n\t}\n\tif check.Analyzer.Doc == \"\" {\n\t\tfmt.Fprintln(os.Stderr, explain, \"has no documentation\")\n\t\treturn 1\n\t}\n\tfmt.Println(check.Doc.Compile())\n\tfmt.Println(\"Online documentation\\n    https://staticcheck.dev/docs/checks#\" + check.Analyzer.Name)\n\treturn 0\n}\n\nfunc (cmd *Command) merge() int {\n\tvar runs []run\n\tif len(cmd.flags.fs.Args()) == 0 {\n\t\tvar err error\n\t\truns, err = decodeGob(bufio.NewReader(os.Stdin))\n\t\tif err != nil {\n\t\t\tfmt.Fprintln(os.Stderr, fmt.Errorf(\"couldn't parse stdin: %s\", err))\n\t\t\treturn 1\n\t\t}\n\t} else {\n\t\tfor _, path := range cmd.flags.fs.Args() {\n\t\t\tsomeRuns, err := func(path string) ([]run, error) {\n\t\t\t\tf, err := os.Open(path)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tdefer f.Close()\n\t\t\t\tbr := bufio.NewReader(f)\n\t\t\t\treturn decodeGob(br)\n\t\t\t}(path)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, fmt.Errorf(\"couldn't parse file %s: %s\", path, err))\n\t\t\t\treturn 1\n\t\t\t}\n\t\t\truns = append(runs, someRuns...)\n\t\t}\n\t}\n\n\trelevantDiagnostics := mergeRuns(runs)\n\tcs := slices.Collect(maps.Values(cmd.analyzers))\n\treturn cmd.printDiagnostics(cs, relevantDiagnostics)\n}\n\nfunc (cmd *Command) lint() int {\n\tswitch cmd.flags.formatter {\n\tcase \"text\", \"stylish\", \"json\", \"sarif\", \"binary\", \"null\":\n\tdefault:\n\t\tfmt.Fprintf(os.Stderr, \"unsupported output format %q\\n\", cmd.flags.formatter)\n\t\treturn 2\n\t}\n\n\tvar bconfs []buildConfig\n\tif cmd.flags.matrix {\n\t\tif cmd.flags.tags != \"\" {\n\t\t\tfmt.Fprintln(os.Stderr, \"cannot use -matrix and -tags together\")\n\t\t\treturn 2\n\t\t}\n\n\t\tvar err error\n\t\tbconfs, err = parseBuildConfigs(os.Stdin)\n\t\tif err != nil {\n\t\t\tif perr, ok := err.(parseBuildConfigError); ok {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"<stdin>:%d couldn't parse build matrix: %s\\n\", perr.line, perr.err)\n\t\t\t} else {\n\t\t\t\tfmt.Fprintln(os.Stderr, err)\n\t\t\t}\n\t\t\treturn 2\n\t\t}\n\t} else {\n\t\tbc := buildConfig{}\n\t\tif cmd.flags.tags != \"\" {\n\t\t\t// Validate that the tags argument is well-formed. go/packages\n\t\t\t// doesn't detect malformed build flags and returns unhelpful\n\t\t\t// errors.\n\t\t\ttf := buildutil.TagsFlag{}\n\t\t\tif err := tf.Set(cmd.flags.tags); err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, fmt.Errorf(\"invalid value %q for flag -tags: %s\", cmd.flags.tags, err))\n\t\t\t\treturn 1\n\t\t\t}\n\n\t\t\tbc.Flags = []string{\"-tags\", cmd.flags.tags}\n\t\t}\n\t\tbconfs = append(bconfs, bc)\n\t}\n\n\tvar measureAnalyzers func(analysis *analysis.Analyzer, pkg *loader.PackageSpec, d time.Duration)\n\tif path := cmd.flags.debugMeasureAnalyzers; path != \"\" {\n\t\tf, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\n\t\tmu := &sync.Mutex{}\n\t\tmeasureAnalyzers = func(analysis *analysis.Analyzer, pkg *loader.PackageSpec, d time.Duration) {\n\t\t\tmu.Lock()\n\t\t\tdefer mu.Unlock()\n\t\t\t// FIXME(dh): print pkg.ID\n\t\t\tif _, err := fmt.Fprintf(f, \"%s\\t%s\\t%d\\n\", analysis.Name, pkg, d.Nanoseconds()); err != nil {\n\t\t\t\tlog.Println(\"error writing analysis measurements:\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tvar runs []run\n\tcs := slices.Collect(maps.Values(cmd.analyzers))\n\topts := options{\n\t\tanalyzers: cs,\n\t\tpatterns:  cmd.flags.fs.Args(),\n\t\tlintTests: cmd.flags.tests,\n\t\tgoVersion: string(cmd.flags.goVersion),\n\t\tconfig: config.Config{\n\t\t\tChecks: cmd.flags.checks,\n\t\t},\n\t\tprintAnalyzerMeasurement: measureAnalyzers,\n\t}\n\tl, err := newLinter(opts)\n\tif err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t\treturn 1\n\t}\n\tfor _, bconf := range bconfs {\n\t\tres, err := l.run(bconf)\n\t\tif err != nil {\n\t\t\tfmt.Fprintln(os.Stderr, err)\n\t\t\treturn 1\n\t\t}\n\n\t\tfor _, w := range res.Warnings {\n\t\t\tfmt.Fprintln(os.Stderr, \"warning:\", w)\n\t\t}\n\n\t\tcwd, err := os.Getwd()\n\t\tif err != nil {\n\t\t\tcwd = \"\"\n\t\t}\n\t\trelPath := func(s string) string {\n\t\t\tif cwd == \"\" {\n\t\t\t\treturn filepath.ToSlash(s)\n\t\t\t}\n\t\t\tout, err := filepath.Rel(cwd, s)\n\t\t\tif err != nil {\n\t\t\t\treturn filepath.ToSlash(s)\n\t\t\t}\n\t\t\treturn filepath.ToSlash(out)\n\t\t}\n\n\t\tif cmd.flags.formatter == \"binary\" {\n\t\t\tfor i, s := range res.CheckedFiles {\n\t\t\t\tres.CheckedFiles[i] = relPath(s)\n\t\t\t}\n\t\t\tfor i := range res.Diagnostics {\n\t\t\t\t// We turn all paths into relative, /-separated paths. This is to make -merge work correctly when\n\t\t\t\t// merging runs from different OSs, with different absolute paths.\n\t\t\t\t//\n\t\t\t\t// We zero out Offset, because checkouts of code on different OSs may have different kinds of\n\t\t\t\t// newlines and thus different offsets. We don't ever make use of the Offset, anyway. Line and\n\t\t\t\t// column numbers are precomputed.\n\n\t\t\t\td := &res.Diagnostics[i]\n\t\t\t\td.Position.Filename = relPath(d.Position.Filename)\n\t\t\t\td.Position.Offset = 0\n\t\t\t\td.End.Filename = relPath(d.End.Filename)\n\t\t\t\td.End.Offset = 0\n\t\t\t\tfor j := range d.Related {\n\t\t\t\t\tr := &d.Related[j]\n\t\t\t\t\tr.Position.Filename = relPath(r.Position.Filename)\n\t\t\t\t\tr.Position.Offset = 0\n\t\t\t\t\tr.End.Filename = relPath(r.End.Filename)\n\t\t\t\t\tr.End.Offset = 0\n\t\t\t\t}\n\t\t\t}\n\t\t\terr := gob.NewEncoder(os.Stdout).Encode(res)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"failed writing output: %s\\n\", err)\n\t\t\t\treturn 2\n\t\t\t}\n\t\t} else {\n\t\t\truns = append(runs, runFromLintResult(res))\n\t\t}\n\t}\n\n\tl.cache.Trim()\n\n\tif cmd.flags.formatter != \"binary\" {\n\t\tdiags := mergeRuns(runs)\n\t\treturn cmd.printDiagnostics(cs, diags)\n\t}\n\treturn 0\n}\n\nfunc mergeRuns(runs []run) []diagnostic {\n\tvar relevantDiagnostics []diagnostic\n\tfor _, r := range runs {\n\t\tfor _, diag := range r.diagnostics {\n\t\t\tswitch diag.MergeIf {\n\t\t\tcase lint.MergeIfAny:\n\t\t\t\trelevantDiagnostics = append(relevantDiagnostics, diag)\n\t\t\tcase lint.MergeIfAll:\n\t\t\t\tdoPrint := true\n\t\t\t\tfor _, r := range runs {\n\t\t\t\t\tif _, ok := r.checkedFiles[diag.Position.Filename]; ok {\n\t\t\t\t\t\tif _, ok := r.diagnostics[diag.descriptor()]; !ok {\n\t\t\t\t\t\t\tdoPrint = false\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif doPrint {\n\t\t\t\t\trelevantDiagnostics = append(relevantDiagnostics, diag)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn relevantDiagnostics\n}\n\n// printDiagnostics prints the diagnostics and exits the process.\nfunc (cmd *Command) printDiagnostics(cs []*lint.Analyzer, diagnostics []diagnostic) int {\n\tif len(diagnostics) > 1 {\n\t\tsort.Slice(diagnostics, func(i, j int) bool {\n\t\t\tdi := diagnostics[i]\n\t\t\tdj := diagnostics[j]\n\t\t\tpi := di.Position\n\t\t\tpj := dj.Position\n\n\t\t\tif pi.Filename != pj.Filename {\n\t\t\t\treturn pi.Filename < pj.Filename\n\t\t\t}\n\t\t\tif pi.Line != pj.Line {\n\t\t\t\treturn pi.Line < pj.Line\n\t\t\t}\n\t\t\tif pi.Column != pj.Column {\n\t\t\t\treturn pi.Column < pj.Column\n\t\t\t}\n\t\t\tif di.Message != dj.Message {\n\t\t\t\treturn di.Message < dj.Message\n\t\t\t}\n\t\t\tif di.BuildName != dj.BuildName {\n\t\t\t\treturn di.BuildName < dj.BuildName\n\t\t\t}\n\t\t\treturn di.Category < dj.Category\n\t\t})\n\n\t\tfiltered := []diagnostic{\n\t\t\tdiagnostics[0],\n\t\t}\n\t\tbuilds := []map[string]struct{}{\n\t\t\t{diagnostics[0].BuildName: {}},\n\t\t}\n\t\tfor _, diag := range diagnostics[1:] {\n\t\t\t// We may encounter duplicate diagnostics because one file\n\t\t\t// can be part of many packages, and because multiple\n\t\t\t// build configurations may check the same files.\n\t\t\tif !filtered[len(filtered)-1].equal(diag) {\n\t\t\t\tif filtered[len(filtered)-1].descriptor() == diag.descriptor() {\n\t\t\t\t\t// Diagnostics only differ in build name, track new name\n\t\t\t\t\tbuilds[len(filtered)-1][diag.BuildName] = struct{}{}\n\t\t\t\t} else {\n\t\t\t\t\tfiltered = append(filtered, diag)\n\t\t\t\t\tbuilds = append(builds, map[string]struct{}{})\n\t\t\t\t\tbuilds[len(filtered)-1][diag.BuildName] = struct{}{}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvar names []string\n\t\tfor i := range filtered {\n\t\t\tnames = names[:0]\n\t\t\tfor k := range builds[i] {\n\t\t\t\tnames = append(names, k)\n\t\t\t}\n\t\t\tsort.Strings(names)\n\t\t\tfiltered[i].BuildName = strings.Join(names, \",\")\n\t\t}\n\t\tdiagnostics = filtered\n\t}\n\n\tvar f formatter\n\tswitch cmd.flags.formatter {\n\tcase \"text\":\n\t\tf = textFormatter{W: os.Stdout}\n\tcase \"stylish\":\n\t\tf = &stylishFormatter{W: os.Stdout}\n\tcase \"json\":\n\t\tf = jsonFormatter{W: os.Stdout}\n\tcase \"sarif\":\n\t\tf = &sarifFormatter{\n\t\t\tdriverName:    cmd.name,\n\t\t\tdriverVersion: cmd.version,\n\t\t}\n\t\tif cmd.name == \"staticcheck\" {\n\t\t\tf.(*sarifFormatter).driverName = \"Staticcheck\"\n\t\t\tf.(*sarifFormatter).driverWebsite = \"https://staticcheck.dev\"\n\t\t}\n\tcase \"binary\":\n\t\tfmt.Fprintln(os.Stderr, \"'-f binary' not supported in this context\")\n\t\treturn 2\n\tcase \"null\":\n\t\tf = nullFormatter{}\n\tdefault:\n\t\tfmt.Fprintf(os.Stderr, \"unsupported output format %q\\n\", cmd.flags.formatter)\n\t\treturn 2\n\t}\n\n\tfail := cmd.flags.fail\n\tanalyzerNames := make([]string, len(cs))\n\tfor i, a := range cs {\n\t\tanalyzerNames[i] = a.Analyzer.Name\n\t}\n\tshouldExit := filterAnalyzerNames(analyzerNames, fail)\n\tshouldExit[\"staticcheck\"] = true\n\tshouldExit[\"compile\"] = true\n\tshouldExit[\"config\"] = true\n\n\tvar (\n\t\tnumErrors   int\n\t\tnumWarnings int\n\t\tnumIgnored  int\n\t)\n\tnotIgnored := make([]diagnostic, 0, len(diagnostics))\n\tfor _, diag := range diagnostics {\n\t\tif diag.Category == \"compile\" && cmd.flags.debugNoCompileErrors {\n\t\t\tcontinue\n\t\t}\n\t\tif diag.Severity == severityIgnored && !cmd.flags.showIgnored {\n\t\t\tnumIgnored++\n\t\t\tcontinue\n\t\t}\n\t\tif shouldExit[diag.Category] {\n\t\t\tnumErrors++\n\t\t} else {\n\t\t\tdiag.Severity = severityWarning\n\t\t\tnumWarnings++\n\t\t}\n\t\tnotIgnored = append(notIgnored, diag)\n\t}\n\n\tf.Format(cs, notIgnored)\n\tif f, ok := f.(statter); ok {\n\t\tf.Stats(len(diagnostics), numErrors, numWarnings, numIgnored)\n\t}\n\n\tif numErrors > 0 {\n\t\tif _, ok := f.(*sarifFormatter); ok {\n\t\t\t// When emitting SARIF, finding errors is considered success.\n\t\t\treturn 0\n\t\t} else {\n\t\t\treturn 1\n\t\t}\n\t}\n\treturn 0\n}\n\nfunc usage(name string, fs *flag.FlagSet) func() {\n\treturn func() {\n\t\tfmt.Fprintf(os.Stderr, \"Usage: %s [flags] [packages]\\n\", name)\n\n\t\tfmt.Fprintln(os.Stderr)\n\t\tfmt.Fprintln(os.Stderr, \"Flags:\")\n\t\tprintDefaults(fs)\n\n\t\tfmt.Fprintln(os.Stderr)\n\t\tfmt.Fprintln(os.Stderr, \"For help about specifying packages, see 'go help packages'\")\n\t}\n}\n\n// isZeroValue determines whether the string represents the zero\n// value for a flag.\n//\n// this function has been copied from the Go standard library's 'flag' package.\nfunc isZeroValue(f *flag.Flag, value string) bool {\n\t// Build a zero value of the flag's Value type, and see if the\n\t// result of calling its String method equals the value passed in.\n\t// This works unless the Value type is itself an interface type.\n\ttyp := reflect.TypeOf(f.Value)\n\tvar z reflect.Value\n\tif typ.Kind() == reflect.Pointer {\n\t\tz = reflect.New(typ.Elem())\n\t} else {\n\t\tz = reflect.Zero(typ)\n\t}\n\treturn value == z.Interface().(flag.Value).String()\n}\n\n// this function has been copied from the Go standard library's 'flag' package and modified to skip debug flags.\nfunc printDefaults(fs *flag.FlagSet) {\n\tfs.VisitAll(func(f *flag.Flag) {\n\t\t// Don't print debug flags\n\t\tif strings.HasPrefix(f.Name, \"debug.\") {\n\t\t\treturn\n\t\t}\n\n\t\tvar b strings.Builder\n\t\tfmt.Fprintf(&b, \"  -%s\", f.Name) // Two spaces before -; see next two comments.\n\t\tname, usage := flag.UnquoteUsage(f)\n\t\tif len(name) > 0 {\n\t\t\tb.WriteString(\" \")\n\t\t\tb.WriteString(name)\n\t\t}\n\t\t// Boolean flags of one ASCII letter are so common we\n\t\t// treat them specially, putting their usage on the same line.\n\t\tif b.Len() <= 4 { // space, space, '-', 'x'.\n\t\t\tb.WriteString(\"\\t\")\n\t\t} else {\n\t\t\t// Four spaces before the tab triggers good alignment\n\t\t\t// for both 4- and 8-space tab stops.\n\t\t\tb.WriteString(\"\\n    \\t\")\n\t\t}\n\t\tb.WriteString(strings.ReplaceAll(usage, \"\\n\", \"\\n    \\t\"))\n\n\t\tif !isZeroValue(f, f.DefValue) {\n\t\t\tif T := reflect.TypeOf(f.Value); T.Name() == \"*stringValue\" && T.PkgPath() == \"flag\" {\n\t\t\t\t// put quotes on the value\n\t\t\t\tfmt.Fprintf(&b, \" (default %q)\", f.DefValue)\n\t\t\t} else {\n\t\t\t\tfmt.Fprintf(&b, \" (default %v)\", f.DefValue)\n\t\t\t}\n\t\t}\n\t\tfmt.Fprint(fs.Output(), b.String(), \"\\n\")\n\t})\n}\n"
  },
  {
    "path": "lintcmd/cmd_test.go",
    "content": "package lintcmd\n\nimport (\n\t\"go/token\"\n\t\"testing\"\n)\n\nfunc TestParsePos(t *testing.T) {\n\tvar tests = []struct {\n\t\tin  string\n\t\tout token.Position\n\t}{\n\t\t{\n\t\t\t\"/tmp/gopackages280076669/go-build/net/cgo_linux.cgo1.go:1\",\n\t\t\ttoken.Position{\n\t\t\t\tFilename: \"/tmp/gopackages280076669/go-build/net/cgo_linux.cgo1.go\",\n\t\t\t\tLine:     1,\n\t\t\t\tColumn:   0,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"/tmp/gopackages280076669/go-build/net/cgo_linux.cgo1.go:1:\",\n\t\t\ttoken.Position{\n\t\t\t\tFilename: \"/tmp/gopackages280076669/go-build/net/cgo_linux.cgo1.go\",\n\t\t\t\tLine:     1,\n\t\t\t\tColumn:   0,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"/tmp/gopackages280076669/go-build/net/cgo_linux.cgo1.go:23:43\",\n\t\t\ttoken.Position{\n\t\t\t\tFilename: \"/tmp/gopackages280076669/go-build/net/cgo_linux.cgo1.go\",\n\t\t\t\tLine:     23,\n\t\t\t\tColumn:   43,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tres, _, _ := parsePos(tt.in)\n\t\tif res != tt.out {\n\t\t\tt.Errorf(\"failed to parse %q correctly\", tt.in)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lintcmd/config.go",
    "content": "package lintcmd\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"unicode\"\n)\n\ntype parseBuildConfigError struct {\n\tline int\n\terr  error\n}\n\nfunc (err parseBuildConfigError) Error() string { return err.err.Error() }\n\nfunc parseBuildConfigs(r io.Reader) ([]buildConfig, error) {\n\tvar builds []buildConfig\n\tbr := bufio.NewReader(r)\n\ti := 0\n\tfor {\n\t\tline, err := br.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t} else {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tline = strings.TrimSpace(line)\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tname, envs, flags, err := parseBuildConfig(line)\n\t\tif err != nil {\n\t\t\treturn nil, parseBuildConfigError{line: i + 1, err: err}\n\t\t}\n\n\t\tbc := buildConfig{\n\t\t\tName:  name,\n\t\t\tEnvs:  envs,\n\t\t\tFlags: flags,\n\t\t}\n\t\tbuilds = append(builds, bc)\n\n\t\ti++\n\t}\n\treturn builds, nil\n}\n\nfunc parseBuildConfig(line string) (name string, envs []string, flags []string, err error) {\n\tif line == \"\" {\n\t\treturn \"\", nil, nil, errors.New(\"couldn't parse empty build config\")\n\t}\n\tif strings.Index(line, \":\") == len(line)-1 {\n\t\tname = line[:len(line)-1]\n\t} else {\n\t\tbefore, after, ok := strings.Cut(line, \": \")\n\t\tif !ok {\n\t\t\treturn name, envs, flags, errors.New(\"missing build name\")\n\t\t}\n\t\tname = before\n\n\t\tvar buf []rune\n\t\tvar inQuote bool\n\t\targs := &envs\n\t\tfor _, r := range strings.TrimSpace(after) {\n\t\t\tswitch r {\n\t\t\tcase ' ':\n\t\t\t\tif inQuote {\n\t\t\t\t\tbuf = append(buf, r)\n\t\t\t\t} else if len(buf) != 0 {\n\t\t\t\t\tif buf[0] == '-' {\n\t\t\t\t\t\targs = &flags\n\t\t\t\t\t}\n\t\t\t\t\t*args = append(*args, string(buf))\n\t\t\t\t\tbuf = buf[:0]\n\t\t\t\t}\n\t\t\tcase '\"':\n\t\t\t\tinQuote = !inQuote\n\t\t\tdefault:\n\t\t\t\tbuf = append(buf, r)\n\t\t\t}\n\t\t}\n\n\t\tif len(buf) > 0 {\n\t\t\tif inQuote {\n\t\t\t\treturn \"\", nil, nil, errors.New(\"unterminated quoted string\")\n\t\t\t}\n\t\t\tif buf[0] == '-' {\n\t\t\t\targs = &flags\n\t\t\t}\n\t\t\t*args = append(*args, string(buf))\n\t\t}\n\t}\n\n\tfor _, r := range name {\n\t\tif !(r == '_' || unicode.IsLetter(r) || unicode.IsNumber(r)) {\n\t\t\treturn \"\", nil, nil, fmt.Errorf(\"invalid build name %q\", name)\n\t\t}\n\t}\n\treturn name, envs, flags, nil\n}\n"
  },
  {
    "path": "lintcmd/config_test.go",
    "content": "//go:build go1.18\n\npackage lintcmd\n\nimport (\n\t\"testing\"\n)\n\nvar buildConfigTests = []struct {\n\tin      string\n\tname    string\n\tenvs    []string\n\tflags   []string\n\tinvalid bool\n}{\n\t{\n\t\t`some_name: ENV1=foo ENV_2=bar ENV3=\"foo bar baz\" ENV4=foo\"bar\" -flag1 -flag2= -flag3=value -flag4=\"some value\" -flag5=some\" value \"test \"-flag6=1\"`,\n\t\t\"some_name\",\n\t\t[]string{\"ENV1=foo\", \"ENV_2=bar\", \"ENV3=foo bar baz\", \"ENV4=foobar\"},\n\t\t[]string{\"-flag1\", \"-flag2=\", \"-flag3=value\", \"-flag4=some value\", \"-flag5=some value test\", \"-flag6=1\"},\n\t\tfalse,\n\t},\n\t{\n\t\t`some_name: ENV1=foo -tags bar baz=meow`,\n\t\t\"some_name\",\n\t\t[]string{\"ENV1=foo\"},\n\t\t[]string{\"-tags\", \"bar\", \"baz=meow\"},\n\t\tfalse,\n\t},\n\t{\n\t\t\"some_name:\",\n\t\t\"some_name\",\n\t\tnil,\n\t\tnil,\n\t\tfalse,\n\t},\n\t{\n\t\t\"some name:\",\n\t\t\"\",\n\t\tnil,\n\t\tnil,\n\t\ttrue,\n\t},\n\t{\n\t\t\"\",\n\t\t\"\",\n\t\tnil,\n\t\tnil,\n\t\ttrue,\n\t},\n}\n\nfunc FuzzParseBuildConfig(f *testing.F) {\n\tequal := func(a, b []string) bool {\n\t\tif len(a) != len(b) {\n\t\t\treturn false\n\t\t}\n\t\tfor i := range a {\n\t\t\tif a[i] != b[i] {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\n\tfor _, tt := range buildConfigTests {\n\t\tf.Add(tt.in)\n\n\t\tname, envs, flags, err := parseBuildConfig(tt.in)\n\t\tif err != nil {\n\t\t\tif tt.invalid {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tf.Fatalf(\"input %q failed to parse: %s\", tt.in, err)\n\t\t}\n\t\tif tt.invalid {\n\t\t\tf.Fatalf(\"expected input %q to fail but it didn't\", tt.in)\n\t\t}\n\n\t\tif name != tt.name {\n\t\t\tf.Fatalf(\"got name %q, want %q\", name, tt.name)\n\t\t}\n\t\tif !equal(envs, tt.envs) {\n\t\t\tf.Fatalf(\"got environment %#v, want %#v\", envs, tt.envs)\n\t\t}\n\t\tif !equal(flags, tt.flags) {\n\t\t\tf.Fatalf(\"got flags %#v, want %#v\", flags, tt.flags)\n\t\t}\n\t}\n\n\tf.Fuzz(func(t *testing.T, in string) {\n\t\tparseBuildConfig(in)\n\t})\n}\n"
  },
  {
    "path": "lintcmd/directives.go",
    "content": "package lintcmd\n\nimport (\n\t\"strings\"\n\n\t\"honnef.co/go/tools/lintcmd/runner\"\n)\n\nfunc parseDirectives(dirs []runner.SerializedDirective) ([]ignore, []diagnostic) {\n\tvar ignores []ignore\n\tvar diagnostics []diagnostic\n\n\tfor _, dir := range dirs {\n\t\tcmd := dir.Command\n\t\targs := dir.Arguments\n\t\tswitch cmd {\n\t\tcase \"ignore\", \"file-ignore\":\n\t\t\tif len(args) < 2 {\n\t\t\t\tp := diagnostic{\n\t\t\t\t\tDiagnostic: runner.Diagnostic{\n\t\t\t\t\t\tPosition: dir.NodePosition,\n\t\t\t\t\t\tMessage:  \"malformed linter directive; missing the required reason field?\",\n\t\t\t\t\t\tCategory: \"compile\",\n\t\t\t\t\t},\n\t\t\t\t\tSeverity: severityError,\n\t\t\t\t}\n\t\t\t\tdiagnostics = append(diagnostics, p)\n\t\t\t\tcontinue\n\t\t\t}\n\t\tdefault:\n\t\t\t// unknown directive, ignore\n\t\t\tcontinue\n\t\t}\n\t\tchecks := strings.Split(args[0], \",\")\n\t\tpos := dir.NodePosition\n\t\tvar ig ignore\n\t\tswitch cmd {\n\t\tcase \"ignore\":\n\t\t\tig = &lineIgnore{\n\t\t\t\tFile:   pos.Filename,\n\t\t\t\tLine:   pos.Line,\n\t\t\t\tChecks: checks,\n\t\t\t\tPos:    dir.DirectivePosition,\n\t\t\t}\n\t\tcase \"file-ignore\":\n\t\t\tig = &fileIgnore{\n\t\t\t\tFile:   pos.Filename,\n\t\t\t\tChecks: checks,\n\t\t\t}\n\t\t}\n\t\tignores = append(ignores, ig)\n\t}\n\n\treturn ignores, diagnostics\n}\n"
  },
  {
    "path": "lintcmd/format.go",
    "content": "package lintcmd\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"go/token\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"text/tabwriter\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n)\n\nfunc shortPath(path string) string {\n\tcwd, err := os.Getwd()\n\tif err != nil {\n\t\treturn path\n\t}\n\tif rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) {\n\t\treturn rel\n\t}\n\treturn path\n}\n\nfunc relativePositionString(pos token.Position) string {\n\ts := shortPath(pos.Filename)\n\tif pos.IsValid() {\n\t\tif s != \"\" {\n\t\t\ts += \":\"\n\t\t}\n\t\ts += fmt.Sprintf(\"%d:%d\", pos.Line, pos.Column)\n\t}\n\tif s == \"\" {\n\t\ts = \"-\"\n\t}\n\treturn s\n}\n\ntype statter interface {\n\tStats(total, errors, warnings, ignored int)\n}\n\ntype formatter interface {\n\tFormat(checks []*lint.Analyzer, diagnostics []diagnostic)\n}\n\ntype textFormatter struct {\n\tW io.Writer\n}\n\nfunc (o textFormatter) Format(_ []*lint.Analyzer, ps []diagnostic) {\n\tfor _, p := range ps {\n\t\tfmt.Fprintf(o.W, \"%s: %s\\n\", relativePositionString(p.Position), p.String())\n\t\tfor _, r := range p.Related {\n\t\t\tfmt.Fprintf(o.W, \"\\t%s: %s\\n\", relativePositionString(r.Position), r.Message)\n\t\t}\n\t}\n}\n\ntype nullFormatter struct{}\n\nfunc (nullFormatter) Format([]*lint.Analyzer, []diagnostic) {}\n\ntype jsonFormatter struct {\n\tW io.Writer\n}\n\nfunc (o jsonFormatter) Format(_ []*lint.Analyzer, ps []diagnostic) {\n\ttype location struct {\n\t\tFile   string `json:\"file\"`\n\t\tLine   int    `json:\"line\"`\n\t\tColumn int    `json:\"column\"`\n\t}\n\ttype related struct {\n\t\tLocation location `json:\"location\"`\n\t\tEnd      location `json:\"end\"`\n\t\tMessage  string   `json:\"message\"`\n\t}\n\n\tenc := json.NewEncoder(o.W)\n\tfor _, p := range ps {\n\t\tjp := struct {\n\t\t\tCode     string    `json:\"code\"`\n\t\t\tSeverity string    `json:\"severity,omitempty\"`\n\t\t\tLocation location  `json:\"location\"`\n\t\t\tEnd      location  `json:\"end\"`\n\t\t\tMessage  string    `json:\"message\"`\n\t\t\tRelated  []related `json:\"related,omitempty\"`\n\t\t}{\n\t\t\tCode:     p.Category,\n\t\t\tSeverity: p.Severity.String(),\n\t\t\tLocation: location{\n\t\t\t\tFile:   p.Position.Filename,\n\t\t\t\tLine:   p.Position.Line,\n\t\t\t\tColumn: p.Position.Column,\n\t\t\t},\n\t\t\tEnd: location{\n\t\t\t\tFile:   p.End.Filename,\n\t\t\t\tLine:   p.End.Line,\n\t\t\t\tColumn: p.End.Column,\n\t\t\t},\n\t\t\tMessage: p.Message,\n\t\t}\n\t\tfor _, r := range p.Related {\n\t\t\tjp.Related = append(jp.Related, related{\n\t\t\t\tLocation: location{\n\t\t\t\t\tFile:   r.Position.Filename,\n\t\t\t\t\tLine:   r.Position.Line,\n\t\t\t\t\tColumn: r.Position.Column,\n\t\t\t\t},\n\t\t\t\tEnd: location{\n\t\t\t\t\tFile:   r.End.Filename,\n\t\t\t\t\tLine:   r.End.Line,\n\t\t\t\t\tColumn: r.End.Column,\n\t\t\t\t},\n\t\t\t\tMessage: r.Message,\n\t\t\t})\n\t\t}\n\t\t_ = enc.Encode(jp)\n\t}\n}\n\ntype stylishFormatter struct {\n\tW io.Writer\n\n\tprevFile string\n\ttw       *tabwriter.Writer\n}\n\nfunc (o *stylishFormatter) Format(_ []*lint.Analyzer, ps []diagnostic) {\n\tfor _, p := range ps {\n\t\tpos := p.Position\n\t\tif pos.Filename == \"\" {\n\t\t\tpos.Filename = \"-\"\n\t\t}\n\n\t\tif pos.Filename != o.prevFile {\n\t\t\tif o.prevFile != \"\" {\n\t\t\t\to.tw.Flush()\n\t\t\t\tfmt.Fprintln(o.W)\n\t\t\t}\n\t\t\tfmt.Fprintln(o.W, pos.Filename)\n\t\t\to.prevFile = pos.Filename\n\t\t\to.tw = tabwriter.NewWriter(o.W, 0, 4, 2, ' ', 0)\n\t\t}\n\t\tfmt.Fprintf(o.tw, \"  (%d, %d)\\t%s\\t%s\\n\", pos.Line, pos.Column, p.Category, p.Message)\n\t\tfor _, r := range p.Related {\n\t\t\tfmt.Fprintf(o.tw, \"    (%d, %d)\\t\\t  %s\\n\", r.Position.Line, r.Position.Column, r.Message)\n\t\t}\n\t}\n}\n\nfunc (o *stylishFormatter) Stats(total, errors, warnings, ignored int) {\n\tif o.tw != nil {\n\t\to.tw.Flush()\n\t\tfmt.Fprintln(o.W)\n\t}\n\tfmt.Fprintf(o.W, \" ✖ %d problems (%d errors, %d warnings, %d ignored)\\n\",\n\t\ttotal, errors, warnings, ignored)\n}\n"
  },
  {
    "path": "lintcmd/lint.go",
    "content": "package lintcmd\n\nimport (\n\t\"crypto/sha256\"\n\t\"fmt\"\n\t\"go/token\"\n\t\"io\"\n\t\"os\"\n\t\"os/signal\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/config\"\n\t\"honnef.co/go/tools/go/buildid\"\n\t\"honnef.co/go/tools/go/loader\"\n\t\"honnef.co/go/tools/lintcmd/cache\"\n\t\"honnef.co/go/tools/lintcmd/runner\"\n\t\"honnef.co/go/tools/unused\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/packages\"\n)\n\n// A linter lints Go source code.\ntype linter struct {\n\tanalyzers map[string]*lint.Analyzer\n\tcache     *cache.Cache\n\topts      options\n}\n\nfunc computeSalt() ([]byte, error) {\n\tp, err := os.Executable()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif id, err := buildid.ReadFile(p); err == nil {\n\t\treturn []byte(id), nil\n\t} else {\n\t\t// For some reason we couldn't read the build id from the executable.\n\t\t// Fall back to hashing the entire executable.\n\t\tf, err := os.Open(p)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer f.Close()\n\t\th := sha256.New()\n\t\tif _, err := io.Copy(h, f); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn h.Sum(nil), nil\n\t}\n}\n\nfunc newLinter(opts options) (*linter, error) {\n\tc, err := cache.Default()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsalt, err := computeSalt()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not compute salt for cache: %s\", err)\n\t}\n\tc.SetSalt(salt)\n\n\tanalyzers := make(map[string]*lint.Analyzer, len(opts.analyzers))\n\tfor _, a := range opts.analyzers {\n\t\tanalyzers[a.Analyzer.Name] = a\n\t}\n\n\treturn &linter{\n\t\tcache:     c,\n\t\tanalyzers: analyzers,\n\t\topts:      opts,\n\t}, nil\n}\n\ntype lintResult struct {\n\t// These fields are exported so that we can gob encode them.\n\n\tCheckedFiles []string\n\tDiagnostics  []diagnostic\n\tWarnings     []string\n}\n\ntype options struct {\n\tconfig                   config.Config\n\tanalyzers                []*lint.Analyzer\n\tpatterns                 []string\n\tlintTests                bool\n\tgoVersion                string\n\tprintAnalyzerMeasurement func(analysis *analysis.Analyzer, pkg *loader.PackageSpec, d time.Duration)\n}\n\nfunc (l *linter) run(bconf buildConfig) (lintResult, error) {\n\tcfg := &packages.Config{}\n\tif l.opts.lintTests {\n\t\tcfg.Tests = true\n\t}\n\n\tcfg.BuildFlags = bconf.Flags\n\tcfg.Env = append(os.Environ(), bconf.Envs...)\n\n\tr, err := runner.New(l.opts.config, l.cache)\n\tif err != nil {\n\t\treturn lintResult{}, err\n\t}\n\tr.GoVersion = l.opts.goVersion\n\tr.Stats.PrintAnalyzerMeasurement = l.opts.printAnalyzerMeasurement\n\n\tprintStats := func() {\n\t\t// Individual stats are read atomically, but overall there\n\t\t// is no synchronisation. For printing rough progress\n\t\t// information, this doesn't matter.\n\t\tswitch r.Stats.State() {\n\t\tcase runner.StateInitializing:\n\t\t\tfmt.Fprintln(os.Stderr, \"Status: initializing\")\n\t\tcase runner.StateLoadPackageGraph:\n\t\t\tfmt.Fprintln(os.Stderr, \"Status: loading package graph\")\n\t\tcase runner.StateBuildActionGraph:\n\t\t\tfmt.Fprintln(os.Stderr, \"Status: building action graph\")\n\t\tcase runner.StateProcessing:\n\t\t\tfmt.Fprintf(os.Stderr, \"Packages: %d/%d initial, %d/%d total; Workers: %d/%d\\n\",\n\t\t\t\tr.Stats.ProcessedInitialPackages(),\n\t\t\t\tr.Stats.InitialPackages(),\n\t\t\t\tr.Stats.ProcessedPackages(),\n\t\t\t\tr.Stats.TotalPackages(),\n\t\t\t\tr.ActiveWorkers(),\n\t\t\t\tr.TotalWorkers(),\n\t\t\t)\n\t\tcase runner.StateFinalizing:\n\t\t\tfmt.Fprintln(os.Stderr, \"Status: finalizing\")\n\t\t}\n\t}\n\tif len(infoSignals) > 0 {\n\t\tch := make(chan os.Signal, 1)\n\t\tsignal.Notify(ch, infoSignals...)\n\t\tdefer signal.Stop(ch)\n\t\tgo func() {\n\t\t\tfor range ch {\n\t\t\t\tprintStats()\n\t\t\t}\n\t\t}()\n\t}\n\tres, err := l.lint(r, cfg, l.opts.patterns)\n\tfor i := range res.Diagnostics {\n\t\tres.Diagnostics[i].BuildName = bconf.Name\n\t}\n\treturn res, err\n}\n\nfunc (l *linter) lint(r *runner.Runner, cfg *packages.Config, patterns []string) (lintResult, error) {\n\tvar out lintResult\n\n\tas := make([]*analysis.Analyzer, 0, len(l.analyzers))\n\tfor _, a := range l.analyzers {\n\t\tas = append(as, a.Analyzer)\n\t}\n\tresults, err := r.Run(cfg, as, patterns)\n\tif err != nil {\n\t\treturn out, err\n\t}\n\n\tif len(results) == 0 {\n\t\t// TODO(dh): emulate Go's behavior more closely once we have\n\t\t// access to go list's Match field.\n\t\tfor _, pattern := range patterns {\n\t\t\tfmt.Fprintf(os.Stderr, \"warning: %q matched no packages\\n\", pattern)\n\t\t}\n\t}\n\n\tanalyzerNames := make([]string, 0, len(l.analyzers))\n\tfor name := range l.analyzers {\n\t\tanalyzerNames = append(analyzerNames, name)\n\t}\n\tused := map[unusedKey]bool{}\n\tvar unuseds []unusedPair\n\tfor _, res := range results {\n\t\tif len(res.Errors) > 0 && !res.Failed {\n\t\t\tpanic(\"package has errors but isn't marked as failed\")\n\t\t}\n\t\tif res.Failed {\n\t\t\tout.Diagnostics = append(out.Diagnostics, failed(res)...)\n\t\t} else {\n\t\t\tif res.Skipped {\n\t\t\t\tout.Warnings = append(out.Warnings, fmt.Sprintf(\"skipped package %s because it is too large\", res.Package))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif !res.Initial {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tout.CheckedFiles = append(out.CheckedFiles, res.Package.GoFiles...)\n\t\t\tallowedAnalyzers := filterAnalyzerNames(analyzerNames, res.Config.Checks)\n\t\t\tresd, err := res.Load()\n\t\t\tif err != nil {\n\t\t\t\treturn out, err\n\t\t\t}\n\t\t\tps := success(allowedAnalyzers, resd)\n\t\t\tfiltered, err := filterIgnored(ps, resd, allowedAnalyzers)\n\t\t\tif err != nil {\n\t\t\t\treturn out, err\n\t\t\t}\n\t\t\t// OPT move this code into the 'success' function.\n\t\t\tfor i, diag := range filtered {\n\t\t\t\ta := l.analyzers[diag.Category]\n\t\t\t\t// Some diag.Category don't map to analyzers, such as \"staticcheck\"\n\t\t\t\tif a != nil {\n\t\t\t\t\tfiltered[i].MergeIf = a.Doc.MergeIf\n\t\t\t\t}\n\t\t\t}\n\t\t\tout.Diagnostics = append(out.Diagnostics, filtered...)\n\n\t\t\tfor _, obj := range resd.Unused.Used {\n\t\t\t\t// Note: a side-effect of this code is that fields in instantiated structs are handled correctly. Even\n\t\t\t\t// if only an instantiated field is marked as used, we will not flag the generic field, because it has\n\t\t\t\t// the same position as the instance. At some point this won't be necessary anymore because we'll be\n\t\t\t\t// able to make use of the Go 1.19+ Origin methods.\n\n\t\t\t\t// FIXME(dh): pick the object whose filename does not include $GOROOT\n\t\t\t\tkey := unusedKey{\n\t\t\t\t\tpkgPath: res.Package.PkgPath,\n\t\t\t\t\tbase:    filepath.Base(obj.Position.Filename),\n\t\t\t\t\tline:    obj.Position.Line,\n\t\t\t\t\tname:    obj.Name,\n\t\t\t\t}\n\t\t\t\tused[key] = true\n\t\t\t}\n\n\t\t\tif allowedAnalyzers[\"U1000\"] {\n\t\t\t\tfor _, obj := range resd.Unused.Unused {\n\t\t\t\t\tkey := unusedKey{\n\t\t\t\t\t\tpkgPath: res.Package.PkgPath,\n\t\t\t\t\t\tbase:    filepath.Base(obj.Position.Filename),\n\t\t\t\t\t\tline:    obj.Position.Line,\n\t\t\t\t\t\tname:    obj.Name,\n\t\t\t\t\t}\n\t\t\t\t\tunuseds = append(unuseds, unusedPair{key, obj})\n\t\t\t\t\tif _, ok := used[key]; !ok {\n\t\t\t\t\t\tused[key] = false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, uo := range unuseds {\n\t\tif used[uo.key] {\n\t\t\tcontinue\n\t\t}\n\t\tout.Diagnostics = append(out.Diagnostics, diagnostic{\n\t\t\tDiagnostic: runner.Diagnostic{\n\t\t\t\tPosition: uo.obj.DisplayPosition,\n\t\t\t\tMessage:  fmt.Sprintf(\"%s %s is unused\", uo.obj.Kind, uo.obj.Name),\n\t\t\t\tCategory: \"U1000\",\n\t\t\t},\n\t\t\tMergeIf: lint.MergeIfAll,\n\t\t})\n\t}\n\n\treturn out, nil\n}\n\nfunc filterIgnored(diagnostics []diagnostic, res runner.ResultData, allowedAnalyzers map[string]bool) ([]diagnostic, error) {\n\tcouldHaveMatched := func(ig *lineIgnore) bool {\n\t\tfor _, c := range ig.Checks {\n\t\t\tif c == \"U1000\" {\n\t\t\t\t// We never want to flag ignores for U1000,\n\t\t\t\t// because U1000 isn't local to a single\n\t\t\t\t// package. For example, an identifier may\n\t\t\t\t// only be used by tests, in which case an\n\t\t\t\t// ignore would only fire when not analyzing\n\t\t\t\t// tests. To avoid spurious \"useless ignore\"\n\t\t\t\t// warnings, just never flag U1000.\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Even though the runner always runs all analyzers, we\n\t\t\t// still only flag unmatched ignores for the set of\n\t\t\t// analyzers the user has expressed interest in. That way,\n\t\t\t// `staticcheck -checks=SA1000` won't complain about an\n\t\t\t// unmatched ignore for an unrelated check.\n\t\t\tif allowedAnalyzers[c] {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\treturn false\n\t}\n\n\tignores, moreDiagnostics := parseDirectives(res.Directives)\n\n\tfor _, ig := range ignores {\n\t\tfor i := range diagnostics {\n\t\t\tdiag := &diagnostics[i]\n\t\t\tif ig.match(*diag) {\n\t\t\t\tdiag.Severity = severityIgnored\n\t\t\t}\n\t\t}\n\n\t\tif ig, ok := ig.(*lineIgnore); ok && !ig.Matched && couldHaveMatched(ig) {\n\t\t\tdiag := diagnostic{\n\t\t\t\tDiagnostic: runner.Diagnostic{\n\t\t\t\t\tPosition: ig.Pos,\n\t\t\t\t\tMessage:  \"this linter directive didn't match anything; should it be removed?\",\n\t\t\t\t\tCategory: \"staticcheck\",\n\t\t\t\t},\n\t\t\t}\n\t\t\tmoreDiagnostics = append(moreDiagnostics, diag)\n\t\t}\n\t}\n\n\treturn append(diagnostics, moreDiagnostics...), nil\n}\n\ntype ignore interface {\n\tmatch(diag diagnostic) bool\n}\n\ntype lineIgnore struct {\n\tFile    string\n\tLine    int\n\tChecks  []string\n\tMatched bool\n\tPos     token.Position\n}\n\nfunc (li *lineIgnore) match(p diagnostic) bool {\n\tpos := p.Position\n\tif pos.Filename != li.File || pos.Line != li.Line {\n\t\treturn false\n\t}\n\tfor _, c := range li.Checks {\n\t\tif m, _ := filepath.Match(c, p.Category); m {\n\t\t\tli.Matched = true\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (li *lineIgnore) String() string {\n\tmatched := \"not matched\"\n\tif li.Matched {\n\t\tmatched = \"matched\"\n\t}\n\treturn fmt.Sprintf(\"%s:%d %s (%s)\", li.File, li.Line, strings.Join(li.Checks, \", \"), matched)\n}\n\ntype fileIgnore struct {\n\tFile   string\n\tChecks []string\n}\n\nfunc (fi *fileIgnore) match(p diagnostic) bool {\n\tif p.Position.Filename != fi.File {\n\t\treturn false\n\t}\n\tfor _, c := range fi.Checks {\n\t\tif m, _ := filepath.Match(c, p.Category); m {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\ntype severity uint8\n\nconst (\n\tseverityError severity = iota\n\tseverityWarning\n\tseverityIgnored\n)\n\nfunc (s severity) String() string {\n\tswitch s {\n\tcase severityError:\n\t\treturn \"error\"\n\tcase severityWarning:\n\t\treturn \"warning\"\n\tcase severityIgnored:\n\t\treturn \"ignored\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"Severity(%d)\", s)\n\t}\n}\n\n// diagnostic represents a diagnostic in some source code.\ntype diagnostic struct {\n\trunner.Diagnostic\n\n\t// These fields are exported so that we can gob encode them.\n\tSeverity  severity\n\tMergeIf   lint.MergeStrategy\n\tBuildName string\n}\n\nfunc (p diagnostic) equal(o diagnostic) bool {\n\treturn p.Position == o.Position &&\n\t\tp.End == o.End &&\n\t\tp.Message == o.Message &&\n\t\tp.Category == o.Category &&\n\t\tp.Severity == o.Severity &&\n\t\tp.MergeIf == o.MergeIf &&\n\t\tp.BuildName == o.BuildName\n}\n\nfunc (p *diagnostic) String() string {\n\tif p.BuildName != \"\" {\n\t\treturn fmt.Sprintf(\"%s [%s] (%s)\", p.Message, p.BuildName, p.Category)\n\t} else {\n\t\treturn fmt.Sprintf(\"%s (%s)\", p.Message, p.Category)\n\t}\n}\n\nfunc failed(res runner.Result) []diagnostic {\n\tvar diagnostics []diagnostic\n\n\tfor _, e := range res.Errors {\n\t\tswitch e := e.(type) {\n\t\tcase packages.Error:\n\t\t\tmsg := e.Msg\n\t\t\tif len(msg) != 0 && msg[0] == '\\n' {\n\t\t\t\t// TODO(dh): See https://github.com/golang/go/issues/32363\n\t\t\t\tmsg = msg[1:]\n\t\t\t}\n\n\t\t\tcat := \"compile\"\n\t\t\tif e.Kind == packages.ParseError {\n\t\t\t\tcat = \"config\"\n\t\t\t}\n\n\t\t\tvar posn token.Position\n\t\t\tif e.Pos == \"\" {\n\t\t\t\t// Under certain conditions (malformed package\n\t\t\t\t// declarations, multiple packages in the same\n\t\t\t\t// directory), go list emits an error on stderr\n\t\t\t\t// instead of JSON. Those errors do not have\n\t\t\t\t// associated position information in\n\t\t\t\t// go/packages.Error, even though the output on\n\t\t\t\t// stderr may contain it.\n\t\t\t\tif p, n, err := parsePos(msg); err == nil {\n\t\t\t\t\tif abs, err := filepath.Abs(p.Filename); err == nil {\n\t\t\t\t\t\tp.Filename = abs\n\t\t\t\t\t}\n\t\t\t\t\tposn = p\n\t\t\t\t\tmsg = msg[n+2:]\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar err error\n\t\t\t\tposn, _, err = parsePos(e.Pos)\n\t\t\t\tif err != nil {\n\t\t\t\t\tpanic(fmt.Sprintf(\"internal error: %s\", err))\n\t\t\t\t}\n\t\t\t}\n\t\t\tdiag := diagnostic{\n\t\t\t\tDiagnostic: runner.Diagnostic{\n\t\t\t\t\tPosition: posn,\n\t\t\t\t\tMessage:  msg,\n\t\t\t\t\tCategory: cat,\n\t\t\t\t},\n\t\t\t\tSeverity: severityError,\n\t\t\t}\n\t\t\tdiagnostics = append(diagnostics, diag)\n\t\tcase error:\n\t\t\tdiag := diagnostic{\n\t\t\t\tDiagnostic: runner.Diagnostic{\n\t\t\t\t\tPosition: token.Position{},\n\t\t\t\t\tMessage:  e.Error(),\n\t\t\t\t\tCategory: \"compile\",\n\t\t\t\t},\n\t\t\t\tSeverity: severityError,\n\t\t\t}\n\t\t\tdiagnostics = append(diagnostics, diag)\n\t\t}\n\t}\n\n\treturn diagnostics\n}\n\ntype unusedKey struct {\n\tpkgPath string\n\tbase    string\n\tline    int\n\tname    string\n}\n\ntype unusedPair struct {\n\tkey unusedKey\n\tobj unused.Object\n}\n\nfunc success(allowedAnalyzers map[string]bool, res runner.ResultData) []diagnostic {\n\tdiags := res.Diagnostics\n\tvar diagnostics []diagnostic\n\tfor _, diag := range diags {\n\t\tif !allowedAnalyzers[diag.Category] {\n\t\t\tcontinue\n\t\t}\n\t\tdiagnostics = append(diagnostics, diagnostic{Diagnostic: diag})\n\t}\n\treturn diagnostics\n}\n\nfunc filterAnalyzerNames(analyzers []string, checks []string) map[string]bool {\n\tallowedChecks := map[string]bool{}\n\n\tfor _, check := range checks {\n\t\tb := true\n\t\tif len(check) > 1 && check[0] == '-' {\n\t\t\tb = false\n\t\t\tcheck = check[1:]\n\t\t}\n\t\tif check == \"*\" || check == \"all\" {\n\t\t\t// Match all\n\t\t\tfor _, c := range analyzers {\n\t\t\t\tallowedChecks[c] = b\n\t\t\t}\n\t\t} else if strings.HasSuffix(check, \"*\") {\n\t\t\t// Glob\n\t\t\tprefix := check[:len(check)-1]\n\t\t\tisCat := strings.IndexFunc(prefix, func(r rune) bool { return unicode.IsNumber(r) }) == -1\n\n\t\t\tfor _, a := range analyzers {\n\t\t\t\tidx := strings.IndexFunc(a, func(r rune) bool { return unicode.IsNumber(r) })\n\t\t\t\tif isCat {\n\t\t\t\t\t// Glob is S*, which should match S1000 but not SA1000\n\t\t\t\t\tcat := a[:idx]\n\t\t\t\t\tif prefix == cat {\n\t\t\t\t\t\tallowedChecks[a] = b\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Glob is S1*\n\t\t\t\t\tif strings.HasPrefix(a, prefix) {\n\t\t\t\t\t\tallowedChecks[a] = b\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Literal check name\n\t\t\tallowedChecks[check] = b\n\t\t}\n\t}\n\treturn allowedChecks\n}\n\n// Note that the file name is optional and can be empty because of //line\n// directives of the form \"//line :1\" (but not \"//line :1:1\"). See\n// https://go.dev/issue/24183 and https://staticcheck.dev/issues/1582.\nvar posRe = regexp.MustCompile(`^(?:(.+?):)?(\\d+)(?::(\\d+)?)?`)\n\nfunc parsePos(pos string) (token.Position, int, error) {\n\tif pos == \"-\" || pos == \"\" {\n\t\treturn token.Position{}, 0, nil\n\t}\n\tparts := posRe.FindStringSubmatch(pos)\n\tif parts == nil {\n\t\treturn token.Position{}, 0, fmt.Errorf(\"malformed position %q\", pos)\n\t}\n\tfile := parts[1]\n\tline, _ := strconv.Atoi(parts[2])\n\tcol, _ := strconv.Atoi(parts[3])\n\treturn token.Position{\n\t\tFilename: file,\n\t\tLine:     line,\n\t\tColumn:   col,\n\t}, len(parts[0]), nil\n}\n"
  },
  {
    "path": "lintcmd/runner/runner.go",
    "content": "// Package runner implements a go/analysis runner. It makes heavy use\n// of on-disk caching to reduce overall memory usage and to speed up\n// repeat runs.\n//\n// # Public API\n//\n// A Runner maps a list of analyzers and package patterns to a list of\n// results. Results provide access to diagnostics, directives, errors\n// encountered, and information about packages. Results explicitly do\n// not contain ASTs or type information. All position information is\n// returned in the form of token.Position, not token.Pos. All work\n// that requires access to the loaded representation of a package has\n// to occur inside analyzers.\n//\n// # Planning and execution\n//\n// Analyzing packages is split into two phases: planning and\n// execution.\n//\n// During planning, a directed acyclic graph of package dependencies\n// is computed. We materialize the full graph so that we can execute\n// the graph from the bottom up, without keeping unnecessary data in\n// memory during a DFS and with simplified parallel execution.\n//\n// During execution, leaf nodes (nodes with no outstanding\n// dependencies) get executed in parallel, bounded by a semaphore\n// sized according to the number of CPUs. Conceptually, this happens\n// in a loop, processing new leaf nodes as they appear, until no more\n// nodes are left. In the actual implementation, nodes know their\n// dependents, and the last dependency of a node to be processed is\n// responsible for scheduling its dependent.\n//\n// The graph is rooted at a synthetic root node. Upon execution of the\n// root node, the algorithm terminates.\n//\n// Analyzing a package repeats the same planning + execution steps,\n// but this time on a graph of analyzers for the package. Parallel\n// execution of individual analyzers is bounded by the same semaphore\n// as executing packages.\n//\n// # Parallelism\n//\n// Actions are executed in parallel where the dependency graph allows.\n// Overall parallelism is bounded by a semaphore, sized according to\n// GOMAXPROCS. Each concurrently processed package takes up a\n// token, as does each analyzer – but a package can always execute at\n// least one analyzer, using the package's token.\n//\n// Depending on the overall shape of the graph, there may be GOMAXPROCS\n// packages running a single analyzer each, a single package running\n// GOMAXPROCS analyzers, or anything in between.\n//\n// Total memory consumption grows roughly linearly with the number of\n// CPUs, while total execution time is inversely proportional to the\n// number of CPUs. Overall, parallelism is affected by the shape of\n// the dependency graph. A lot of inter-connected packages will see\n// less parallelism than a lot of independent packages.\n//\n// # Caching\n//\n// The runner caches facts, directives and diagnostics in a\n// content-addressable cache that is designed after Go's own cache.\n// Additionally, it makes use of Go's export data.\n//\n// This cache not only speeds up repeat runs, it also reduces peak\n// memory usage. When we've analyzed a package, we cache the results\n// and drop them from memory. When a dependent needs any of this\n// information, or when analysis is complete and we wish to render the\n// results, the data gets loaded from disk again.\n//\n// Data only exists in memory when it is immediately needed, not\n// retained for possible future uses. This trades increased CPU usage\n// for reduced memory usage. A single dependency may be loaded many\n// times over, but it greatly reduces peak memory usage, as an\n// arbitrary amount of time may pass between analyzing a dependency\n// and its dependent, during which other packages will be processed.\npackage runner\n\n// OPT(dh): we could reduce disk storage usage of cached data by\n// compressing it, either directly at the cache layer, or by feeding\n// compressed data to the cache. Of course doing so may negatively\n// affect CPU usage, and there are lower hanging fruit, such as\n// needing to cache less data in the first place.\n\n// OPT(dh): right now, each package is analyzed completely\n// independently. Each package loads all of its dependencies from\n// export data and cached facts. If we have two packages A and B,\n// which both depend on C, and which both get analyzed in parallel,\n// then C will be loaded twice. This wastes CPU time and memory. It\n// would be nice if we could reuse a single C for the analysis of both\n// A and B.\n//\n// We can't reuse the actual types.Package or facts, because each\n// package gets its own token.FileSet. Sharing a global FileSet has\n// several drawbacks, including increased memory usage and running the\n// risk of running out of FileSet address space.\n//\n// We could however avoid loading the same raw export data from disk\n// twice, as well as deserializing gob data twice. One possible\n// solution would be a duplicate-suppressing in-memory cache that\n// caches data for a limited amount of time. When the same package\n// needs to be loaded twice in close succession, we can reuse work,\n// without holding unnecessary data in memory for an extended period\n// of time.\n//\n// We would likely need to do extensive benchmarking to figure out how\n// long to keep data around to find a sweet spot where we reduce CPU\n// load without increasing memory usage.\n//\n// We can probably populate the cache after we've analyzed a package,\n// on the assumption that it will have to be loaded again in the near\n// future.\n\nimport (\n\t\"bytes\"\n\t\"encoding/gob\"\n\t\"fmt\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"io\"\n\t\"maps\"\n\t\"os\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/config\"\n\t\"honnef.co/go/tools/go/loader\"\n\ttsync \"honnef.co/go/tools/internal/sync\"\n\t\"honnef.co/go/tools/lintcmd/cache\"\n\t\"honnef.co/go/tools/unused\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/packages\"\n\t\"golang.org/x/tools/go/types/objectpath\"\n)\n\nconst sanityCheck = false\n\n// Diagnostic is like go/analysis.Diagnostic, but with all token.Pos resolved to token.Position.\ntype Diagnostic struct {\n\tPosition token.Position\n\tEnd      token.Position\n\tCategory string\n\tMessage  string\n\n\tSuggestedFixes []SuggestedFix\n\tRelated        []RelatedInformation\n}\n\n// RelatedInformation provides additional context for a diagnostic.\ntype RelatedInformation struct {\n\tPosition token.Position\n\tEnd      token.Position\n\tMessage  string\n}\n\ntype SuggestedFix struct {\n\tMessage   string\n\tTextEdits []TextEdit\n}\n\ntype TextEdit struct {\n\tPosition token.Position\n\tEnd      token.Position\n\tNewText  []byte\n}\n\n// A Result describes the result of analyzing a single package.\n//\n// It holds references to cached diagnostics and directives. They can\n// be loaded on demand with the Load method.\ntype Result struct {\n\tPackage *loader.PackageSpec\n\tConfig  config.Config\n\tInitial bool\n\tSkipped bool\n\n\tFailed bool\n\tErrors []error\n\t// Action results, path to file\n\tresults string\n\t// Results relevant to testing, only set when test mode is enabled, path to file\n\ttestData string\n}\n\ntype SerializedDirective struct {\n\tCommand   string\n\tArguments []string\n\t// The position of the comment\n\tDirectivePosition token.Position\n\t// The position of the node that the comment is attached to\n\tNodePosition token.Position\n}\n\nfunc serializeDirective(dir lint.Directive, fset *token.FileSet) SerializedDirective {\n\treturn SerializedDirective{\n\t\tCommand:           dir.Command,\n\t\tArguments:         dir.Arguments,\n\t\tDirectivePosition: report.DisplayPosition(fset, dir.Directive.Pos()),\n\t\tNodePosition:      report.DisplayPosition(fset, dir.Node.Pos()),\n\t}\n}\n\ntype ResultData struct {\n\tDirectives  []SerializedDirective\n\tDiagnostics []Diagnostic\n\tUnused      unused.Result\n}\n\nfunc (r Result) Load() (ResultData, error) {\n\tif r.Failed {\n\t\tpanic(\"Load called on failed Result\")\n\t}\n\tif r.results == \"\" {\n\t\t// this package was only a dependency\n\t\treturn ResultData{}, nil\n\t}\n\tf, err := os.Open(r.results)\n\tif err != nil {\n\t\treturn ResultData{}, fmt.Errorf(\"failed loading result: %w\", err)\n\t}\n\tdefer f.Close()\n\tvar out ResultData\n\terr = gob.NewDecoder(f).Decode(&out)\n\treturn out, err\n}\n\n// TestData contains extra information about analysis runs that is only available in test mode.\ntype TestData struct {\n\t// Facts contains facts produced by analyzers for a package.\n\t// Unlike vetx, this list only contains facts specific to this package,\n\t// not all facts for the transitive closure of dependencies.\n\tFacts []TestFact\n\t// List of files that were part of the package.\n\tFiles []string\n}\n\n// LoadTest returns data relevant to testing.\n// It should only be called if Runner.TestMode was set to true.\nfunc (r Result) LoadTest() (TestData, error) {\n\tif r.Failed {\n\t\tpanic(\"Load called on failed Result\")\n\t}\n\tif r.results == \"\" {\n\t\t// this package was only a dependency\n\t\treturn TestData{}, nil\n\t}\n\tf, err := os.Open(r.testData)\n\tif err != nil {\n\t\treturn TestData{}, fmt.Errorf(\"failed loading test data: %w\", err)\n\t}\n\tdefer f.Close()\n\tvar out TestData\n\terr = gob.NewDecoder(f).Decode(&out)\n\treturn out, err\n}\n\ntype action interface {\n\tDeps() []action\n\tTriggers() []action\n\tDecrementPending() bool\n\tMarkFailed()\n\tIsFailed() bool\n\tAddError(error)\n}\n\ntype baseAction struct {\n\t// Action description\n\n\tdeps     []action\n\ttriggers []action\n\tpending  uint32\n\n\t// Action results\n\n\t// failed is set to true if the action couldn't be processed. This\n\t// may either be due to an error specific to this action, in\n\t// which case the errors field will be populated, or due to a\n\t// dependency being marked as failed, in which case errors will be\n\t// empty.\n\tfailed bool\n\terrors []error\n}\n\nfunc (act *baseAction) Deps() []action     { return act.deps }\nfunc (act *baseAction) Triggers() []action { return act.triggers }\nfunc (act *baseAction) DecrementPending() bool {\n\treturn atomic.AddUint32(&act.pending, ^uint32(0)) == 0\n}\nfunc (act *baseAction) MarkFailed()        { act.failed = true }\nfunc (act *baseAction) IsFailed() bool     { return act.failed }\nfunc (act *baseAction) AddError(err error) { act.errors = append(act.errors, err) }\n\n// packageAction describes the act of loading a package, fully\n// analyzing it, and storing the results.\ntype packageAction struct {\n\tbaseAction\n\n\t// Action description\n\tPackage   *loader.PackageSpec\n\tfactsOnly bool\n\thash      cache.ActionID\n\n\t// Action results\n\tcfg      config.Config\n\tvetx     string\n\tresults  string\n\ttestData string\n\tskipped  bool\n}\n\nfunc (act *packageAction) String() string {\n\treturn fmt.Sprintf(\"packageAction(%s)\", act.Package)\n}\n\ntype objectFact struct {\n\tfact analysis.Fact\n\t// TODO(dh): why do we store the objectpath when producing the\n\t// fact? Is it just for the sanity checking, which compares the\n\t// stored path with a path recomputed from objectFactKey.Obj?\n\tpath objectpath.Path\n}\n\ntype objectFactKey struct {\n\tObj  types.Object\n\tType reflect.Type\n}\n\ntype packageFactKey struct {\n\tPkg  *types.Package\n\tType reflect.Type\n}\n\ntype gobFact struct {\n\tPkgPath string\n\tObjPath string\n\tFact    analysis.Fact\n}\n\n// TestFact is a serialization of facts that is specific to the test mode.\ntype TestFact struct {\n\tObjectName string\n\tPosition   token.Position\n\tFactString string\n\tAnalyzer   string\n}\n\n// analyzerAction describes the act of analyzing a package with a\n// single analyzer.\ntype analyzerAction struct {\n\tbaseAction\n\n\t// Action description\n\n\tAnalyzer *analysis.Analyzer\n\n\t// Action results\n\n\t// We can store actual results here without worrying about memory\n\t// consumption because analyzer actions get garbage collected once\n\t// a package has been fully analyzed.\n\tResult       any\n\tDiagnostics  []Diagnostic\n\tObjectFacts  map[objectFactKey]objectFact\n\tPackageFacts map[packageFactKey]analysis.Fact\n\tPass         *analysis.Pass\n}\n\nfunc (act *analyzerAction) String() string {\n\treturn fmt.Sprintf(\"analyzerAction(%s)\", act.Analyzer)\n}\n\n// A Runner executes analyzers on packages.\ntype Runner struct {\n\tStats     Stats\n\tGoVersion string\n\n\t// If set to true, Runner will populate results with data relevant to testing analyzers\n\tTestMode bool\n\n\t// Config that gets merged with per-package configs\n\tcfg       config.Config\n\tcache     *cache.Cache\n\tsemaphore tsync.Semaphore\n}\n\ntype subrunner struct {\n\t*Runner\n\tanalyzers     []*analysis.Analyzer\n\tfactAnalyzers []*analysis.Analyzer\n\tanalyzerNames string\n\tcache         *cache.Cache\n}\n\n// New returns a new Runner.\nfunc New(cfg config.Config, c *cache.Cache) (*Runner, error) {\n\treturn &Runner{\n\t\tcfg:       cfg,\n\t\tcache:     c,\n\t\tsemaphore: tsync.NewSemaphore(runtime.GOMAXPROCS(0)),\n\t}, nil\n}\n\nfunc newSubrunner(r *Runner, analyzers []*analysis.Analyzer) *subrunner {\n\tanalyzerNames := make([]string, len(analyzers))\n\tfor i, a := range analyzers {\n\t\tanalyzerNames[i] = a.Name\n\t}\n\tsort.Strings(analyzerNames)\n\n\tvar factAnalyzers []*analysis.Analyzer\n\tfor _, a := range analyzers {\n\t\tif len(a.FactTypes) > 0 {\n\t\t\tfactAnalyzers = append(factAnalyzers, a)\n\t\t}\n\t}\n\treturn &subrunner{\n\t\tRunner:        r,\n\t\tanalyzers:     analyzers,\n\t\tfactAnalyzers: factAnalyzers,\n\t\tanalyzerNames: strings.Join(analyzerNames, \",\"),\n\t\tcache:         r.cache,\n\t}\n}\n\nfunc newPackageActionRoot(pkg *loader.PackageSpec, cache map[*loader.PackageSpec]*packageAction) *packageAction {\n\ta := newPackageAction(pkg, cache)\n\ta.factsOnly = false\n\treturn a\n}\n\nfunc newPackageAction(pkg *loader.PackageSpec, cache map[*loader.PackageSpec]*packageAction) *packageAction {\n\tif a, ok := cache[pkg]; ok {\n\t\treturn a\n\t}\n\n\ta := &packageAction{\n\t\tPackage:   pkg,\n\t\tfactsOnly: true, // will be overwritten by any call to Action\n\t}\n\tcache[pkg] = a\n\n\tif len(pkg.Errors) > 0 {\n\t\ta.errors = make([]error, len(pkg.Errors))\n\t\tfor i, err := range pkg.Errors {\n\t\t\ta.errors[i] = err\n\t\t}\n\t\ta.failed = true\n\n\t\t// We don't need to process our imports if this package is\n\t\t// already broken.\n\t\treturn a\n\t}\n\n\ta.deps = make([]action, 0, len(pkg.Imports))\n\tfor _, dep := range pkg.Imports {\n\t\tdepa := newPackageAction(dep, cache)\n\t\tdepa.triggers = append(depa.triggers, a)\n\t\ta.deps = append(a.deps, depa)\n\n\t\tif depa.failed {\n\t\t\ta.failed = true\n\t\t}\n\t}\n\t// sort dependencies because the list of dependencies is part of\n\t// the cache key\n\tsort.Slice(a.deps, func(i, j int) bool {\n\t\treturn a.deps[i].(*packageAction).Package.ID < a.deps[j].(*packageAction).Package.ID\n\t})\n\n\ta.pending = uint32(len(a.deps))\n\n\treturn a\n}\n\nfunc newAnalyzerAction(an *analysis.Analyzer, cache map[*analysis.Analyzer]*analyzerAction) *analyzerAction {\n\tif a, ok := cache[an]; ok {\n\t\treturn a\n\t}\n\n\ta := &analyzerAction{\n\t\tAnalyzer:     an,\n\t\tObjectFacts:  map[objectFactKey]objectFact{},\n\t\tPackageFacts: map[packageFactKey]analysis.Fact{},\n\t}\n\tcache[an] = a\n\tfor _, dep := range an.Requires {\n\t\tdepa := newAnalyzerAction(dep, cache)\n\t\tdepa.triggers = append(depa.triggers, a)\n\t\ta.deps = append(a.deps, depa)\n\t}\n\ta.pending = uint32(len(a.deps))\n\treturn a\n}\n\nfunc getCachedFiles(cache *cache.Cache, ids []cache.ActionID, out []*string) error {\n\tfor i, id := range ids {\n\t\tvar err error\n\t\t*out[i], _, err = cache.GetFile(id)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (r *subrunner) do(act action) error {\n\ta := act.(*packageAction)\n\tdefer func() {\n\t\tr.Stats.finishPackage()\n\t\tif !a.factsOnly {\n\t\t\tr.Stats.finishInitialPackage()\n\t\t}\n\t}()\n\n\t// compute hash of action\n\ta.cfg = a.Package.Config.Merge(r.cfg)\n\th := r.cache.NewHash(\"staticcheck \" + a.Package.PkgPath)\n\n\t// Note that we do not filter the list of analyzers by the\n\t// package's configuration. We don't allow configuration to\n\t// accidentally break dependencies between analyzers, and it's\n\t// easier to always run all checks and filter the output. This\n\t// also makes cached data more reusable.\n\n\t// OPT(dh): not all changes in configuration invalidate cached\n\t// data. specifically, when a.factsOnly == true, we only care\n\t// about checks that produce facts, and settings that affect those\n\t// checks.\n\n\t// Config used for constructing the hash; this config doesn't have\n\t// Checks populated, because we always run all checks.\n\t//\n\t// This even works for users who add custom checks, because we include the binary's hash.\n\thashCfg := a.cfg\n\thashCfg.Checks = nil\n\t// note that we don't hash staticcheck's version; it is set as the\n\t// salt by a package main.\n\tfmt.Fprintf(h, \"cfg %#v\\n\", hashCfg)\n\tfmt.Fprintf(h, \"pkg %x\\n\", a.Package.Hash)\n\tfmt.Fprintf(h, \"analyzers %s\\n\", r.analyzerNames)\n\tfmt.Fprintf(h, \"go %s\\n\", r.GoVersion)\n\tfmt.Fprintf(h, \"env godebug %q\\n\", os.Getenv(\"GODEBUG\"))\n\n\t// OPT(dh): do we actually need to hash vetx? can we not assume\n\t// that for identical inputs, staticcheck will produce identical\n\t// vetx?\n\tfor _, dep := range a.deps {\n\t\tdep := dep.(*packageAction)\n\t\tvetxHash, err := cache.FileHash(dep.vetx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed computing hash: %w\", err)\n\t\t}\n\t\tfmt.Fprintf(h, \"vetout %q %x\\n\", dep.Package.PkgPath, vetxHash)\n\t}\n\ta.hash = cache.ActionID(h.Sum())\n\n\t// try to fetch hashed data\n\tids := make([]cache.ActionID, 0, 2)\n\tids = append(ids, cache.Subkey(a.hash, \"vetx\"))\n\tif !a.factsOnly {\n\t\tids = append(ids, cache.Subkey(a.hash, \"results\"))\n\t\tif r.TestMode {\n\t\t\tids = append(ids, cache.Subkey(a.hash, \"testdata\"))\n\t\t}\n\t}\n\tif err := getCachedFiles(r.cache, ids, []*string{&a.vetx, &a.results, &a.testData}); err != nil {\n\t\tresult, err := r.doUncached(a)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif a.failed {\n\t\t\treturn nil\n\t\t}\n\n\t\ta.skipped = result.skipped\n\n\t\t// OPT(dh) instead of collecting all object facts and encoding\n\t\t// them after analysis finishes, we could encode them as we\n\t\t// go. however, that would require some locking.\n\t\t//\n\t\t// OPT(dh): We could sort gobFacts for more consistent output,\n\t\t// but it doesn't matter. The hash of a package includes all\n\t\t// of its files, so whether the vetx hash changes or not, a\n\t\t// change to a package requires re-analyzing all dependents,\n\t\t// even if the vetx data stayed the same. See also the note at\n\t\t// the top of loader/hash.go.\n\n\t\ttf := &bytes.Buffer{}\n\t\tenc := gob.NewEncoder(tf)\n\t\tfor _, gf := range result.facts {\n\t\t\tif err := enc.Encode(gf); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed gob encoding data: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\ta.vetx, err = r.writeCacheReader(a, \"vetx\", bytes.NewReader(tf.Bytes()))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif a.factsOnly {\n\t\t\treturn nil\n\t\t}\n\n\t\tvar out ResultData\n\t\tout.Directives = make([]SerializedDirective, len(result.dirs))\n\t\tfor i, dir := range result.dirs {\n\t\t\tout.Directives[i] = serializeDirective(dir, result.lpkg.Fset)\n\t\t}\n\n\t\tout.Diagnostics = result.diags\n\t\tout.Unused = result.unused\n\t\ta.results, err = r.writeCacheGob(a, \"results\", out)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif r.TestMode {\n\t\t\tout := TestData{\n\t\t\t\tFacts: result.testFacts,\n\t\t\t\tFiles: result.lpkg.GoFiles,\n\t\t\t}\n\t\t\ta.testData, err = r.writeCacheGob(a, \"testdata\", out)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// ActiveWorkers returns the number of currently running workers.\nfunc (r *Runner) ActiveWorkers() int {\n\treturn r.semaphore.Len()\n}\n\n// TotalWorkers returns the maximum number of possible workers.\nfunc (r *Runner) TotalWorkers() int {\n\treturn r.semaphore.Cap()\n}\n\nfunc (r *Runner) writeCacheReader(a *packageAction, kind string, rs io.ReadSeeker) (string, error) {\n\th := cache.Subkey(a.hash, kind)\n\tout, _, err := r.cache.Put(h, rs)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed caching data: %w\", err)\n\t}\n\treturn r.cache.OutputFile(out), nil\n}\n\nfunc (r *Runner) writeCacheGob(a *packageAction, kind string, data any) (string, error) {\n\tf, err := os.CreateTemp(\"\", \"staticcheck\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer f.Close()\n\tos.Remove(f.Name())\n\tif err := gob.NewEncoder(f).Encode(data); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed gob encoding data: %w\", err)\n\t}\n\tif _, err := f.Seek(0, io.SeekStart); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn r.writeCacheReader(a, kind, f)\n}\n\ntype packageActionResult struct {\n\tfacts   []gobFact\n\tdiags   []Diagnostic\n\tunused  unused.Result\n\tdirs    []lint.Directive\n\tlpkg    *loader.Package\n\tskipped bool\n\n\t// Only set when using test mode\n\ttestFacts []TestFact\n}\n\nfunc (r *subrunner) doUncached(a *packageAction) (packageActionResult, error) {\n\t// OPT(dh): for a -> b; c -> b; if both a and b are being\n\t// processed concurrently, we shouldn't load b's export data\n\t// twice.\n\n\tpkg, _, err := loader.Load(a.Package, &loader.Options{GoVersion: r.GoVersion})\n\tif err != nil {\n\t\treturn packageActionResult{}, err\n\t}\n\n\tif len(pkg.Errors) > 0 {\n\t\t// this handles errors that occurred during type-checking the\n\t\t// package in loader.Load\n\t\tfor _, err := range pkg.Errors {\n\t\t\ta.errors = append(a.errors, err)\n\t\t}\n\t\ta.failed = true\n\t\treturn packageActionResult{}, nil\n\t}\n\n\tif len(pkg.Syntax) == 0 && pkg.PkgPath != \"unsafe\" {\n\t\treturn packageActionResult{lpkg: pkg, skipped: true}, nil\n\t}\n\n\t// OPT(dh): instead of parsing directives twice (twice because\n\t// U1000 depends on the facts.Directives analyzer), reuse the\n\t// existing result\n\tvar dirs []lint.Directive\n\tif !a.factsOnly {\n\t\tdirs = lint.ParseDirectives(pkg.Syntax, pkg.Fset)\n\t}\n\tres, err := r.runAnalyzers(a, pkg)\n\n\treturn packageActionResult{\n\t\tfacts:     res.facts,\n\t\ttestFacts: res.testFacts,\n\t\tdiags:     res.diagnostics,\n\t\tunused:    res.unused,\n\t\tdirs:      dirs,\n\t\tlpkg:      pkg,\n\t}, err\n}\n\nfunc pkgPaths(root *types.Package) map[string]*types.Package {\n\tout := map[string]*types.Package{}\n\tvar dfs func(*types.Package)\n\tdfs = func(pkg *types.Package) {\n\t\tif _, ok := out[pkg.Path()]; ok {\n\t\t\treturn\n\t\t}\n\t\tout[pkg.Path()] = pkg\n\t\tfor _, imp := range pkg.Imports() {\n\t\t\tdfs(imp)\n\t\t}\n\t}\n\tdfs(root)\n\treturn out\n}\n\nfunc (r *Runner) loadFacts(root *types.Package, dep *packageAction, objFacts map[objectFactKey]objectFact, pkgFacts map[packageFactKey]analysis.Fact) error {\n\t// Load facts of all imported packages\n\tvetx, err := os.Open(dep.vetx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed loading cached facts: %w\", err)\n\t}\n\tdefer vetx.Close()\n\n\tpathToPkg := pkgPaths(root)\n\tdec := gob.NewDecoder(vetx)\n\tfor {\n\t\tvar gf gobFact\n\t\terr := dec.Decode(&gf)\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"failed loading cached facts: %w\", err)\n\t\t}\n\n\t\tpkg, ok := pathToPkg[gf.PkgPath]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tif gf.ObjPath == \"\" {\n\t\t\tpkgFacts[packageFactKey{\n\t\t\t\tPkg:  pkg,\n\t\t\t\tType: reflect.TypeOf(gf.Fact),\n\t\t\t}] = gf.Fact\n\t\t} else {\n\t\t\tobj, err := objectpath.Object(pkg, objectpath.Path(gf.ObjPath))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tobjFacts[objectFactKey{\n\t\t\t\tObj:  obj,\n\t\t\t\tType: reflect.TypeOf(gf.Fact),\n\t\t\t}] = objectFact{gf.Fact, objectpath.Path(gf.ObjPath)}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc genericHandle(a action, root action, queue chan action, sem *tsync.Semaphore, exec func(a action) error) {\n\tif a == root {\n\t\tclose(queue)\n\t\tif sem != nil {\n\t\t\tsem.Release()\n\t\t}\n\t\treturn\n\t}\n\tif !a.IsFailed() {\n\t\t// the action may have already been marked as failed during\n\t\t// construction of the action graph, for example because of\n\t\t// unresolved imports.\n\n\t\tfor _, dep := range a.Deps() {\n\t\t\tif dep.IsFailed() {\n\t\t\t\t// One of our dependencies failed, so mark this package as\n\t\t\t\t// failed and bail. We don't need to record an error for\n\t\t\t\t// this package, the relevant error will have been\n\t\t\t\t// reported by the first package in the chain that failed.\n\t\t\t\ta.MarkFailed()\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tif !a.IsFailed() {\n\t\tif err := exec(a); err != nil {\n\t\t\ta.MarkFailed()\n\t\t\ta.AddError(err)\n\t\t}\n\t}\n\tif sem != nil {\n\t\tsem.Release()\n\t}\n\n\tfor _, t := range a.Triggers() {\n\t\tif t.DecrementPending() {\n\t\t\tqueue <- t\n\t\t}\n\t}\n}\n\ntype analyzerRunner struct {\n\tpkg *loader.Package\n\t// object facts of our dependencies; may contain facts of\n\t// analyzers other than the current one\n\tdepObjFacts map[objectFactKey]objectFact\n\t// package facts of our dependencies; may contain facts of\n\t// analyzers other than the current one\n\tdepPkgFacts map[packageFactKey]analysis.Fact\n\tfactsOnly   bool\n\n\tstats *Stats\n}\n\nfunc (ar *analyzerRunner) do(act action) error {\n\ta := act.(*analyzerAction)\n\tresults := map[*analysis.Analyzer]any{}\n\t// TODO(dh): does this have to be recursive?\n\tfor _, dep := range a.deps {\n\t\tdep := dep.(*analyzerAction)\n\t\tresults[dep.Analyzer] = dep.Result\n\t}\n\t// OPT(dh): cache factTypes, it is the same for all packages for a given analyzer\n\t//\n\t// OPT(dh): do we need the factTypes map? most analyzers have 0-1\n\t// fact types. iterating over the slice is probably faster than\n\t// indexing a map.\n\tfactTypes := map[reflect.Type]struct{}{}\n\tfor _, typ := range a.Analyzer.FactTypes {\n\t\tfactTypes[reflect.TypeOf(typ)] = struct{}{}\n\t}\n\tfilterFactType := func(typ reflect.Type) bool {\n\t\t_, ok := factTypes[typ]\n\t\treturn ok\n\t}\n\ta.Pass = &analysis.Pass{\n\t\tAnalyzer:   a.Analyzer,\n\t\tFset:       ar.pkg.Fset,\n\t\tFiles:      ar.pkg.Syntax,\n\t\tOtherFiles: ar.pkg.OtherFiles,\n\t\tPkg:        ar.pkg.Types,\n\t\tTypesInfo:  ar.pkg.TypesInfo,\n\t\tTypesSizes: ar.pkg.TypesSizes,\n\t\tReport: func(diag analysis.Diagnostic) {\n\t\t\tif !ar.factsOnly {\n\t\t\t\tif diag.Category == \"\" {\n\t\t\t\t\tdiag.Category = a.Analyzer.Name\n\t\t\t\t}\n\t\t\t\td := Diagnostic{\n\t\t\t\t\tPosition: report.DisplayPosition(ar.pkg.Fset, diag.Pos),\n\t\t\t\t\tEnd:      report.DisplayPosition(ar.pkg.Fset, diag.End),\n\t\t\t\t\tCategory: diag.Category,\n\t\t\t\t\tMessage:  diag.Message,\n\t\t\t\t}\n\t\t\t\tfor _, sugg := range diag.SuggestedFixes {\n\t\t\t\t\ts := SuggestedFix{\n\t\t\t\t\t\tMessage: sugg.Message,\n\t\t\t\t\t}\n\t\t\t\t\tfor _, edit := range sugg.TextEdits {\n\t\t\t\t\t\ts.TextEdits = append(s.TextEdits, TextEdit{\n\t\t\t\t\t\t\tPosition: report.DisplayPosition(ar.pkg.Fset, edit.Pos),\n\t\t\t\t\t\t\tEnd:      report.DisplayPosition(ar.pkg.Fset, edit.End),\n\t\t\t\t\t\t\tNewText:  edit.NewText,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\td.SuggestedFixes = append(d.SuggestedFixes, s)\n\t\t\t\t}\n\t\t\t\tfor _, rel := range diag.Related {\n\t\t\t\t\td.Related = append(d.Related, RelatedInformation{\n\t\t\t\t\t\tPosition: report.DisplayPosition(ar.pkg.Fset, rel.Pos),\n\t\t\t\t\t\tEnd:      report.DisplayPosition(ar.pkg.Fset, rel.End),\n\t\t\t\t\t\tMessage:  rel.Message,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\ta.Diagnostics = append(a.Diagnostics, d)\n\t\t\t}\n\t\t},\n\t\tResultOf: results,\n\t\tImportObjectFact: func(obj types.Object, fact analysis.Fact) bool {\n\t\t\tkey := objectFactKey{\n\t\t\t\tObj:  obj,\n\t\t\t\tType: reflect.TypeOf(fact),\n\t\t\t}\n\t\t\tif f, ok := ar.depObjFacts[key]; ok {\n\t\t\t\treflect.ValueOf(fact).Elem().Set(reflect.ValueOf(f.fact).Elem())\n\t\t\t\treturn true\n\t\t\t} else if f, ok := a.ObjectFacts[key]; ok {\n\t\t\t\treflect.ValueOf(fact).Elem().Set(reflect.ValueOf(f.fact).Elem())\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t\tImportPackageFact: func(pkg *types.Package, fact analysis.Fact) bool {\n\t\t\tkey := packageFactKey{\n\t\t\t\tPkg:  pkg,\n\t\t\t\tType: reflect.TypeOf(fact),\n\t\t\t}\n\t\t\tif f, ok := ar.depPkgFacts[key]; ok {\n\t\t\t\treflect.ValueOf(fact).Elem().Set(reflect.ValueOf(f).Elem())\n\t\t\t\treturn true\n\t\t\t} else if f, ok := a.PackageFacts[key]; ok {\n\t\t\t\treflect.ValueOf(fact).Elem().Set(reflect.ValueOf(f).Elem())\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t\tExportObjectFact: func(obj types.Object, fact analysis.Fact) {\n\t\t\tkey := objectFactKey{\n\t\t\t\tObj:  obj,\n\t\t\t\tType: reflect.TypeOf(fact),\n\t\t\t}\n\t\t\tpath, _ := objectpath.For(obj)\n\t\t\ta.ObjectFacts[key] = objectFact{fact, path}\n\t\t},\n\t\tExportPackageFact: func(fact analysis.Fact) {\n\t\t\tkey := packageFactKey{\n\t\t\t\tPkg:  ar.pkg.Types,\n\t\t\t\tType: reflect.TypeOf(fact),\n\t\t\t}\n\t\t\ta.PackageFacts[key] = fact\n\t\t},\n\t\tAllPackageFacts: func() []analysis.PackageFact {\n\t\t\tout := make([]analysis.PackageFact, 0, len(ar.depPkgFacts)+len(a.PackageFacts))\n\t\t\tfor key, fact := range ar.depPkgFacts {\n\t\t\t\tout = append(out, analysis.PackageFact{\n\t\t\t\t\tPackage: key.Pkg,\n\t\t\t\t\tFact:    fact,\n\t\t\t\t})\n\t\t\t}\n\t\t\tfor key, fact := range a.PackageFacts {\n\t\t\t\tout = append(out, analysis.PackageFact{\n\t\t\t\t\tPackage: key.Pkg,\n\t\t\t\t\tFact:    fact,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\t\tAllObjectFacts: func() []analysis.ObjectFact {\n\t\t\tout := make([]analysis.ObjectFact, 0, len(ar.depObjFacts)+len(a.ObjectFacts))\n\t\t\tfor key, fact := range ar.depObjFacts {\n\t\t\t\tif filterFactType(key.Type) {\n\t\t\t\t\tout = append(out, analysis.ObjectFact{\n\t\t\t\t\t\tObject: key.Obj,\n\t\t\t\t\t\tFact:   fact.fact,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor key, fact := range a.ObjectFacts {\n\t\t\t\tif filterFactType(key.Type) {\n\t\t\t\t\tout = append(out, analysis.ObjectFact{\n\t\t\t\t\t\tObject: key.Obj,\n\t\t\t\t\t\tFact:   fact.fact,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\t}\n\n\tt := time.Now()\n\tres, err := a.Analyzer.Run(a.Pass)\n\tar.stats.measureAnalyzer(a.Analyzer, ar.pkg.PackageSpec, time.Since(t))\n\tif err != nil {\n\t\treturn err\n\t}\n\ta.Result = res\n\treturn nil\n}\n\ntype analysisResult struct {\n\tfacts       []gobFact\n\tdiagnostics []Diagnostic\n\tunused      unused.Result\n\n\t// Only set when using test mode\n\ttestFacts []TestFact\n}\n\nfunc (r *subrunner) runAnalyzers(pkgAct *packageAction, pkg *loader.Package) (analysisResult, error) {\n\tdepObjFacts := map[objectFactKey]objectFact{}\n\tdepPkgFacts := map[packageFactKey]analysis.Fact{}\n\n\tfor _, dep := range pkgAct.deps {\n\t\tif err := r.loadFacts(pkg.Types, dep.(*packageAction), depObjFacts, depPkgFacts); err != nil {\n\t\t\treturn analysisResult{}, err\n\t\t}\n\t}\n\n\troot := &analyzerAction{}\n\tvar analyzers []*analysis.Analyzer\n\tif pkgAct.factsOnly {\n\t\t// When analyzing non-initial packages, we only care about\n\t\t// analyzers that produce facts.\n\t\tanalyzers = r.factAnalyzers\n\t} else {\n\t\tanalyzers = r.analyzers\n\t}\n\n\tall := map[*analysis.Analyzer]*analyzerAction{}\n\tfor _, a := range analyzers {\n\t\ta := newAnalyzerAction(a, all)\n\t\troot.deps = append(root.deps, a)\n\t\ta.triggers = append(a.triggers, root)\n\t}\n\troot.pending = uint32(len(root.deps))\n\n\tar := &analyzerRunner{\n\t\tpkg:         pkg,\n\t\tfactsOnly:   pkgAct.factsOnly,\n\t\tdepObjFacts: depObjFacts,\n\t\tdepPkgFacts: depPkgFacts,\n\t\tstats:       &r.Stats,\n\t}\n\tqueue := make(chan action, len(all))\n\tfor _, a := range all {\n\t\tif len(a.Deps()) == 0 {\n\t\t\tqueue <- a\n\t\t}\n\t}\n\n\t// Don't hang if there are no analyzers to run; for example\n\t// because we are analyzing a dependency but have no analyzers\n\t// that produce facts.\n\tif len(all) == 0 {\n\t\tclose(queue)\n\t}\n\tfor item := range queue {\n\t\tb := r.semaphore.AcquireMaybe()\n\t\tif b {\n\t\t\tgo genericHandle(item, root, queue, &r.semaphore, ar.do)\n\t\t} else {\n\t\t\t// the semaphore is exhausted; run the analysis under the\n\t\t\t// token we've acquired for analyzing the package.\n\t\t\tgenericHandle(item, root, queue, nil, ar.do)\n\t\t}\n\t}\n\n\tvar unusedResult unused.Result\n\tfor _, a := range all {\n\t\tif a != root && a.Analyzer.Name == \"U1000\" && !a.failed {\n\t\t\t// TODO(dh): figure out a clean abstraction, instead of\n\t\t\t// special-casing U1000.\n\t\t\tunusedResult = a.Result.(unused.Result)\n\t\t}\n\n\t\tmaps.Copy(depObjFacts, a.ObjectFacts)\n\t\tmaps.Copy(depPkgFacts, a.PackageFacts)\n\t}\n\n\t// OPT(dh): cull objects not reachable via the exported closure\n\tvar testFacts []TestFact\n\tgobFacts := make([]gobFact, 0, len(depObjFacts)+len(depPkgFacts))\n\tfor key, fact := range depObjFacts {\n\t\tif fact.path == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif sanityCheck {\n\t\t\tp, _ := objectpath.For(key.Obj)\n\t\t\tif p != fact.path {\n\t\t\t\tpanic(fmt.Sprintf(\"got different object paths for %v. old: %q new: %q\", key.Obj, fact.path, p))\n\t\t\t}\n\t\t}\n\t\tgf := gobFact{\n\t\t\tPkgPath: key.Obj.Pkg().Path(),\n\t\t\tObjPath: string(fact.path),\n\t\t\tFact:    fact.fact,\n\t\t}\n\t\tgobFacts = append(gobFacts, gf)\n\t}\n\n\tfor key, fact := range depPkgFacts {\n\t\tgf := gobFact{\n\t\t\tPkgPath: key.Pkg.Path(),\n\t\t\tFact:    fact,\n\t\t}\n\t\tgobFacts = append(gobFacts, gf)\n\t}\n\n\tif r.TestMode {\n\t\tfor _, a := range all {\n\t\t\tfor key, fact := range a.ObjectFacts {\n\t\t\t\ttgf := TestFact{\n\t\t\t\t\tObjectName: key.Obj.Name(),\n\t\t\t\t\tPosition:   pkg.Fset.Position(key.Obj.Pos()),\n\t\t\t\t\tFactString: fmt.Sprint(fact.fact),\n\t\t\t\t\tAnalyzer:   a.Analyzer.Name,\n\t\t\t\t}\n\t\t\t\ttestFacts = append(testFacts, tgf)\n\t\t\t}\n\n\t\t\tfor _, fact := range a.PackageFacts {\n\t\t\t\ttgf := TestFact{\n\t\t\t\t\tObjectName: \"\",\n\t\t\t\t\tPosition:   pkg.Fset.Position(pkg.Syntax[0].Pos()),\n\t\t\t\t\tFactString: fmt.Sprint(fact),\n\t\t\t\t\tAnalyzer:   a.Analyzer.Name,\n\t\t\t\t}\n\t\t\t\ttestFacts = append(testFacts, tgf)\n\t\t\t}\n\t\t}\n\t}\n\n\tvar diags []Diagnostic\n\tfor _, a := range root.deps {\n\t\ta := a.(*analyzerAction)\n\t\tdiags = append(diags, a.Diagnostics...)\n\t}\n\treturn analysisResult{\n\t\tfacts:       gobFacts,\n\t\ttestFacts:   testFacts,\n\t\tdiagnostics: diags,\n\t\tunused:      unusedResult,\n\t}, nil\n}\n\nfunc registerGobTypes(analyzers []*analysis.Analyzer) {\n\tfor _, a := range analyzers {\n\t\tfor _, typ := range a.FactTypes {\n\t\t\t// FIXME(dh): use RegisterName so we can work around collisions\n\t\t\t// in names. For pointer-types, gob incorrectly qualifies\n\t\t\t// type names with the package name, not the import path.\n\t\t\tgob.Register(typ)\n\t\t}\n\t}\n}\n\nfunc allAnalyzers(analyzers []*analysis.Analyzer) []*analysis.Analyzer {\n\tseen := map[*analysis.Analyzer]struct{}{}\n\tout := make([]*analysis.Analyzer, 0, len(analyzers))\n\tvar dfs func(*analysis.Analyzer)\n\tdfs = func(a *analysis.Analyzer) {\n\t\tif _, ok := seen[a]; ok {\n\t\t\treturn\n\t\t}\n\t\tseen[a] = struct{}{}\n\t\tout = append(out, a)\n\t\tfor _, dep := range a.Requires {\n\t\t\tdfs(dep)\n\t\t}\n\t}\n\tfor _, a := range analyzers {\n\t\tdfs(a)\n\t}\n\treturn out\n}\n\n// Run loads the packages specified by patterns, runs analyzers on\n// them and returns the results. Each result corresponds to a single\n// package. Results will be returned for all packages, including\n// dependencies. Errors specific to packages will be reported in the\n// respective results.\n//\n// If cfg is nil, a default config will be used. Otherwise, cfg will\n// be used, with the exception of the Mode field.\nfunc (r *Runner) Run(cfg *packages.Config, analyzers []*analysis.Analyzer, patterns []string) ([]Result, error) {\n\tanalyzers = allAnalyzers(analyzers)\n\tregisterGobTypes(analyzers)\n\n\tr.Stats.setState(StateLoadPackageGraph)\n\tlpkgs, err := loader.Graph(r.cache, cfg, patterns...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tr.Stats.setInitialPackages(len(lpkgs))\n\n\tif len(lpkgs) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tr.Stats.setState(StateBuildActionGraph)\n\tall := map[*loader.PackageSpec]*packageAction{}\n\troot := &packageAction{}\n\tfor _, lpkg := range lpkgs {\n\t\ta := newPackageActionRoot(lpkg, all)\n\t\troot.deps = append(root.deps, a)\n\t\ta.triggers = append(a.triggers, root)\n\t}\n\troot.pending = uint32(len(root.deps))\n\n\tqueue := make(chan action)\n\tr.Stats.setTotalPackages(len(all) - 1)\n\n\tr.Stats.setState(StateProcessing)\n\tgo func() {\n\t\tfor _, a := range all {\n\t\t\tif len(a.Deps()) == 0 {\n\t\t\t\tqueue <- a\n\t\t\t}\n\t\t}\n\t}()\n\n\tsr := newSubrunner(r, analyzers)\n\tfor item := range queue {\n\t\tr.semaphore.Acquire()\n\t\tgo genericHandle(item, root, queue, &r.semaphore, func(act action) error {\n\t\t\treturn sr.do(act)\n\t\t})\n\t}\n\n\tr.Stats.setState(StateFinalizing)\n\tout := make([]Result, 0, len(all))\n\tfor _, item := range all {\n\t\tif item.Package == nil {\n\t\t\tcontinue\n\t\t}\n\t\tout = append(out, Result{\n\t\t\tPackage:  item.Package,\n\t\t\tConfig:   item.cfg,\n\t\t\tInitial:  !item.factsOnly,\n\t\t\tSkipped:  item.skipped,\n\t\t\tFailed:   item.failed,\n\t\t\tErrors:   item.errors,\n\t\t\tresults:  item.results,\n\t\t\ttestData: item.testData,\n\t\t})\n\t}\n\treturn out, nil\n}\n"
  },
  {
    "path": "lintcmd/runner/stats.go",
    "content": "package runner\n\nimport (\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"honnef.co/go/tools/go/loader\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nconst (\n\tStateInitializing = iota\n\tStateLoadPackageGraph\n\tStateBuildActionGraph\n\tStateProcessing\n\tStateFinalizing\n)\n\ntype Stats struct {\n\tstate                    uint32\n\tinitialPackages          uint32\n\ttotalPackages            uint32\n\tprocessedPackages        uint32\n\tprocessedInitialPackages uint32\n\n\t// optional function to call every time an analyzer has finished analyzing a package.\n\tPrintAnalyzerMeasurement func(*analysis.Analyzer, *loader.PackageSpec, time.Duration)\n}\n\nfunc (s *Stats) setState(state uint32)    { atomic.StoreUint32(&s.state, state) }\nfunc (s *Stats) State() int               { return int(atomic.LoadUint32(&s.state)) }\nfunc (s *Stats) setInitialPackages(n int) { atomic.StoreUint32(&s.initialPackages, uint32(n)) }\nfunc (s *Stats) InitialPackages() int     { return int(atomic.LoadUint32(&s.initialPackages)) }\nfunc (s *Stats) setTotalPackages(n int)   { atomic.StoreUint32(&s.totalPackages, uint32(n)) }\nfunc (s *Stats) TotalPackages() int       { return int(atomic.LoadUint32(&s.totalPackages)) }\n\nfunc (s *Stats) finishPackage()         { atomic.AddUint32(&s.processedPackages, 1) }\nfunc (s *Stats) finishInitialPackage()  { atomic.AddUint32(&s.processedInitialPackages, 1) }\nfunc (s *Stats) ProcessedPackages() int { return int(atomic.LoadUint32(&s.processedPackages)) }\nfunc (s *Stats) ProcessedInitialPackages() int {\n\treturn int(atomic.LoadUint32(&s.processedInitialPackages))\n}\n\nfunc (s *Stats) measureAnalyzer(analysis *analysis.Analyzer, pkg *loader.PackageSpec, d time.Duration) {\n\tif s.PrintAnalyzerMeasurement != nil {\n\t\ts.PrintAnalyzerMeasurement(analysis, pkg, d)\n\t}\n}\n"
  },
  {
    "path": "lintcmd/sarif.go",
    "content": "package lintcmd\n\n// Notes on GitHub-specific restrictions:\n//\n// Result.Message needs to either have ID or Text set. Markdown\n// gets ignored. Text isn't treated verbatim however: Markdown\n// formatting gets stripped, except for links.\n//\n// GitHub does not display RelatedLocations. The only way to make\n// use of them is to link to them (via their ID) in the\n// Result.Message. And even then, it will only show the referred\n// line of code, not the message. We can duplicate the messages in\n// the Result.Message, but we can't even indent them, because\n// leading whitespace gets stripped.\n//\n// GitHub does use the Markdown version of rule help, but it\n// renders it the way it renders comments on issues – that is, it\n// turns line breaks into hard line breaks, even though it\n// shouldn't.\n//\n// GitHub doesn't make use of the tool's URI or version, nor of\n// the help URIs of rules.\n//\n// There does not seem to be a way of using SARIF for \"normal\" CI,\n// without results showing up as code scanning alerts. Also, a\n// SARIF file containing only warnings, no errors, will not fail\n// CI by default, but this is configurable.\n// GitHub does display some parts of SARIF results in PRs, but\n// most of the useful parts of SARIF, such as help text of rules,\n// is only accessible via the code scanning alerts, which are only\n// accessible by users with write permissions.\n//\n// Result.Suppressions is being ignored.\n//\n//\n// Notes on other tools\n//\n// VS Code Sarif viewer\n//\n// The Sarif viewer in VS Code displays the full message in the\n// tabular view, removing newlines. That makes our multi-line\n// messages (which we use as a workaround for missing related\n// information) very ugly.\n//\n// Much like GitHub, the Sarif viewer does not make related\n// information visible unless we explicitly refer to it in the\n// message.\n//\n// Suggested fixes are not exposed in any way.\n//\n// It only shows the shortDescription or fullDescription of a\n// rule, not its help. We can't put the help in fullDescription,\n// because the fullDescription isn't meant to be that long. For\n// example, GitHub displays it in a single line, under the\n// shortDescription.\n//\n// VS Code can filter based on Result.Suppressions, but it doesn't\n// display our suppression message. Also, by default, suppressed\n// results get shown, and the column indicating that a result is\n// suppressed is hidden, which makes for a confusing experience.\n//\n// When a rule has only an ID, no name, VS Code displays a\n// prominent dash in place of the name. When the name and ID are\n// identical, it prints both. However, we can't make them\n// identical, as SARIF requires that either the ID and name are\n// different, or that the name is omitted.\n\n// FIXME(dh): we're currently reporting column information using UTF-8\n// byte offsets, not using Unicode code points or UTF-16, which are\n// the only two ways allowed by SARIF.\n\n// TODO(dh) set properties.tags – we can use different tags for the\n// staticcheck, simple, stylecheck and unused checks, so users can\n// filter their results\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/sarif\"\n)\n\ntype sarifFormatter struct {\n\tdriverName    string\n\tdriverVersion string\n\tdriverWebsite string\n}\n\nfunc sarifLevel(severity lint.Severity) string {\n\tswitch severity {\n\tcase lint.SeverityNone:\n\t\t// no configured severity, default to warning\n\t\treturn \"warning\"\n\tcase lint.SeverityError:\n\t\treturn \"error\"\n\tcase lint.SeverityDeprecated:\n\t\treturn \"warning\"\n\tcase lint.SeverityWarning:\n\t\treturn \"warning\"\n\tcase lint.SeverityInfo:\n\t\treturn \"note\"\n\tcase lint.SeverityHint:\n\t\treturn \"note\"\n\tdefault:\n\t\t// unreachable\n\t\treturn \"none\"\n\t}\n}\n\nfunc encodePath(path string) string {\n\treturn (&url.URL{Path: path}).EscapedPath()\n}\n\nfunc sarifURI(path string) string {\n\tu := url.URL{\n\t\tScheme: \"file\",\n\t\tPath:   path,\n\t}\n\treturn u.String()\n}\n\nfunc sarifArtifactLocation(name string) sarif.ArtifactLocation {\n\t// Ideally we use relative paths so that GitHub can resolve them\n\tname = shortPath(name)\n\tif filepath.IsAbs(name) {\n\t\treturn sarif.ArtifactLocation{\n\t\t\tURI: sarifURI(name),\n\t\t}\n\t} else {\n\t\treturn sarif.ArtifactLocation{\n\t\t\tURI:       encodePath(name),\n\t\t\tURIBaseID: \"%SRCROOT%\", // This is specific to GitHub,\n\t\t}\n\t}\n}\n\nfunc sarifFormatText(s string) string {\n\t// GitHub doesn't ignore line breaks, even though it should, so we remove them.\n\n\tvar out strings.Builder\n\tlines := strings.Split(s, \"\\n\")\n\tfor i, line := range lines[:len(lines)-1] {\n\t\tout.WriteString(line)\n\t\tif line == \"\" {\n\t\t\tout.WriteString(\"\\n\")\n\t\t} else {\n\t\t\tnextLine := lines[i+1]\n\t\t\tif nextLine == \"\" || strings.HasPrefix(line, \"> \") || strings.HasPrefix(line, \"    \") {\n\t\t\t\tout.WriteString(\"\\n\")\n\t\t\t} else {\n\t\t\t\tout.WriteString(\" \")\n\t\t\t}\n\t\t}\n\t}\n\tout.WriteString(lines[len(lines)-1])\n\treturn convertCodeBlocks(out.String())\n}\n\nfunc moreCodeFollows(lines []string) bool {\n\tfor _, line := range lines {\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(line, \"    \") {\n\t\t\treturn true\n\t\t} else {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn false\n}\n\nvar alpha = regexp.MustCompile(`^[a-zA-Z ]+$`)\n\nfunc convertCodeBlocks(text string) string {\n\tvar buf strings.Builder\n\tlines := strings.Split(text, \"\\n\")\n\n\tinCode := false\n\tempties := 0\n\tfor i, line := range lines {\n\t\tif inCode {\n\t\t\tif !moreCodeFollows(lines[i:]) {\n\t\t\t\tif inCode {\n\t\t\t\t\tfmt.Fprintln(&buf, \"```\")\n\t\t\t\t\tinCode = false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprevEmpties := empties\n\t\tif line == \"\" && !inCode {\n\t\t\tempties++\n\t\t} else {\n\t\t\tempties = 0\n\t\t}\n\n\t\tif line == \"\" {\n\t\t\tfmt.Fprintln(&buf)\n\t\t\tcontinue\n\t\t}\n\n\t\tif strings.HasPrefix(line, \"    \") {\n\t\t\tline = line[4:]\n\t\t\tif !inCode {\n\t\t\t\tfmt.Fprintln(&buf, \"```go\")\n\t\t\t\tinCode = true\n\t\t\t}\n\t\t}\n\n\t\tonlyAlpha := alpha.MatchString(line)\n\t\tout := line\n\t\tif !inCode && prevEmpties >= 2 && onlyAlpha {\n\t\t\tfmt.Fprintf(&buf, \"## %s\\n\", out)\n\t\t} else {\n\t\t\tfmt.Fprint(&buf, out)\n\t\t\tfmt.Fprintln(&buf)\n\t\t}\n\t}\n\tif inCode {\n\t\tfmt.Fprintln(&buf, \"```\")\n\t}\n\n\treturn buf.String()\n}\n\nfunc (o *sarifFormatter) Format(checks []*lint.Analyzer, diagnostics []diagnostic) {\n\t// TODO(dh): some diagnostics shouldn't be reported as results. For example, when the user specifies a package on the command line that doesn't exist.\n\n\tcwd, _ := os.Getwd()\n\trun := sarif.Run{\n\t\tTool: sarif.Tool{\n\t\t\tDriver: sarif.ToolComponent{\n\t\t\t\tName:           o.driverName,\n\t\t\t\tVersion:        o.driverVersion,\n\t\t\t\tInformationURI: o.driverWebsite,\n\t\t\t},\n\t\t},\n\t\tInvocations: []sarif.Invocation{{\n\t\t\tArguments: os.Args[1:],\n\t\t\tWorkingDirectory: sarif.ArtifactLocation{\n\t\t\t\tURI: sarifURI(cwd),\n\t\t\t},\n\t\t\tExecutionSuccessful: true,\n\t\t}},\n\t}\n\tfor _, c := range checks {\n\t\tdoc := c.Doc.Compile()\n\t\trun.Tool.Driver.Rules = append(run.Tool.Driver.Rules,\n\t\t\tsarif.ReportingDescriptor{\n\t\t\t\t// We don't set Name, as Name and ID mustn't be identical.\n\t\t\t\tID: c.Analyzer.Name,\n\t\t\t\tShortDescription: sarif.Message{\n\t\t\t\t\tText:     doc.Title,\n\t\t\t\t\tMarkdown: doc.TitleMarkdown,\n\t\t\t\t},\n\t\t\t\tHelpURI: \"https://staticcheck.dev/docs/checks#\" + c.Analyzer.Name,\n\t\t\t\t// We use our markdown as the plain text version, too. We\n\t\t\t\t// use very little markdown, primarily quotations,\n\t\t\t\t// indented code blocks and backticks. All of these are\n\t\t\t\t// fine as plain text, too.\n\t\t\t\tHelp: sarif.Message{\n\t\t\t\t\tText:     sarifFormatText(doc.Format(false)),\n\t\t\t\t\tMarkdown: sarifFormatText(doc.FormatMarkdown(false)),\n\t\t\t\t},\n\t\t\t\tDefaultConfiguration: sarif.ReportingConfiguration{\n\t\t\t\t\t// TODO(dh): we could figure out which checks were disabled globally\n\t\t\t\t\tEnabled: true,\n\t\t\t\t\tLevel:   sarifLevel(doc.Severity),\n\t\t\t\t},\n\t\t\t})\n\t}\n\n\tfor _, p := range diagnostics {\n\t\tr := sarif.Result{\n\t\t\tRuleID: p.Category,\n\t\t\tKind:   sarif.Fail,\n\t\t\tMessage: sarif.Message{\n\t\t\t\tText: p.Message,\n\t\t\t},\n\t\t}\n\t\tr.Locations = []sarif.Location{{\n\t\t\tPhysicalLocation: sarif.PhysicalLocation{\n\t\t\t\tArtifactLocation: sarifArtifactLocation(p.Position.Filename),\n\t\t\t\tRegion: sarif.Region{\n\t\t\t\t\tStartLine:   p.Position.Line,\n\t\t\t\t\tStartColumn: p.Position.Column,\n\t\t\t\t\tEndLine:     p.End.Line,\n\t\t\t\t\tEndColumn:   p.End.Column,\n\t\t\t\t},\n\t\t\t},\n\t\t}}\n\t\tfor _, fix := range p.SuggestedFixes {\n\t\t\tsfix := sarif.Fix{\n\t\t\t\tDescription: sarif.Message{\n\t\t\t\t\tText: fix.Message,\n\t\t\t\t},\n\t\t\t}\n\t\t\t// file name -> replacements\n\t\t\tchanges := map[string][]sarif.Replacement{}\n\t\t\tfor _, edit := range fix.TextEdits {\n\t\t\t\tchanges[edit.Position.Filename] = append(changes[edit.Position.Filename], sarif.Replacement{\n\t\t\t\t\tDeletedRegion: sarif.Region{\n\t\t\t\t\t\tStartLine:   edit.Position.Line,\n\t\t\t\t\t\tStartColumn: edit.Position.Column,\n\t\t\t\t\t\tEndLine:     edit.End.Line,\n\t\t\t\t\t\tEndColumn:   edit.End.Column,\n\t\t\t\t\t},\n\t\t\t\t\tInsertedContent: sarif.ArtifactContent{\n\t\t\t\t\t\tText: string(edit.NewText),\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\t\t\tfor path, replacements := range changes {\n\t\t\t\tsfix.ArtifactChanges = append(sfix.ArtifactChanges, sarif.ArtifactChange{\n\t\t\t\t\tArtifactLocation: sarifArtifactLocation(path),\n\t\t\t\t\tReplacements:     replacements,\n\t\t\t\t})\n\t\t\t}\n\t\t\tr.Fixes = append(r.Fixes, sfix)\n\t\t}\n\t\tfor i, related := range p.Related {\n\t\t\tr.Message.Text += fmt.Sprintf(\"\\n\\t[%s](%d)\", related.Message, i+1)\n\n\t\t\tr.RelatedLocations = append(r.RelatedLocations,\n\t\t\t\tsarif.Location{\n\t\t\t\t\tID: i + 1,\n\t\t\t\t\tMessage: &sarif.Message{\n\t\t\t\t\t\tText: related.Message,\n\t\t\t\t\t},\n\t\t\t\t\tPhysicalLocation: sarif.PhysicalLocation{\n\t\t\t\t\t\tArtifactLocation: sarifArtifactLocation(related.Position.Filename),\n\t\t\t\t\t\tRegion: sarif.Region{\n\t\t\t\t\t\t\tStartLine:   related.Position.Line,\n\t\t\t\t\t\t\tStartColumn: related.Position.Column,\n\t\t\t\t\t\t\tEndLine:     related.End.Line,\n\t\t\t\t\t\t\tEndColumn:   related.End.Column,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t}\n\n\t\tif p.Severity == severityIgnored {\n\t\t\t// Note that GitHub does not support suppressions, which is why Staticcheck still requires the -show-ignored flag to be set for us to emit ignored diagnostics.\n\n\t\t\tr.Suppressions = []sarif.Suppression{{\n\t\t\t\tKind: \"inSource\",\n\t\t\t\t// TODO(dh): populate the Justification field\n\t\t\t}}\n\t\t} else {\n\t\t\t// We want an empty slice, not nil. SARIF differentiates\n\t\t\t// between the two. An empty slice means that the diagnostic\n\t\t\t// wasn't suppressed, while nil means that we don't have the\n\t\t\t// information available.\n\t\t\tr.Suppressions = []sarif.Suppression{}\n\t\t}\n\t\trun.Results = append(run.Results, r)\n\t}\n\n\tjson.NewEncoder(os.Stdout).Encode(sarif.Log{\n\t\tVersion: sarif.Version,\n\t\tSchema:  sarif.Schema,\n\t\tRuns:    []sarif.Run{run},\n\t})\n}\n"
  },
  {
    "path": "lintcmd/stats.go",
    "content": "//go:build !aix && !android && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris\n\npackage lintcmd\n\nimport \"os\"\n\nvar infoSignals = []os.Signal{}\n"
  },
  {
    "path": "lintcmd/stats_bsd.go",
    "content": "//go:build darwin || dragonfly || freebsd || netbsd || openbsd\n\npackage lintcmd\n\nimport (\n\t\"os\"\n\t\"syscall\"\n)\n\nvar infoSignals = []os.Signal{syscall.SIGINFO}\n"
  },
  {
    "path": "lintcmd/stats_posix.go",
    "content": "//go:build aix || android || linux || solaris\n\npackage lintcmd\n\nimport (\n\t\"os\"\n\t\"syscall\"\n)\n\nvar infoSignals = []os.Signal{syscall.SIGUSR1}\n"
  },
  {
    "path": "lintcmd/version/buildinfo.go",
    "content": "package version\n\nimport (\n\t\"fmt\"\n\t\"runtime/debug\"\n)\n\nfunc printBuildInfo() {\n\tif info, ok := debug.ReadBuildInfo(); ok {\n\t\tfmt.Println(\"Main module:\")\n\t\tprintModule(&info.Main)\n\t\tfmt.Println(\"Dependencies:\")\n\t\tfor _, dep := range info.Deps {\n\t\t\tprintModule(dep)\n\t\t}\n\t} else {\n\t\tfmt.Println(\"Built without Go modules\")\n\t}\n}\n\nfunc buildInfoVersion() (string, bool) {\n\tinfo, ok := debug.ReadBuildInfo()\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\tif info.Main.Version == \"(devel)\" {\n\t\treturn \"\", false\n\t}\n\treturn info.Main.Version, true\n}\n\nfunc printModule(m *debug.Module) {\n\tfmt.Printf(\"\\t%s\", m.Path)\n\tif m.Version != \"(devel)\" {\n\t\tfmt.Printf(\"@%s\", m.Version)\n\t}\n\tif m.Sum != \"\" {\n\t\tfmt.Printf(\" (sum: %s)\", m.Sum)\n\t}\n\tif m.Replace != nil {\n\t\tfmt.Printf(\" (replace: %s)\", m.Replace.Path)\n\t}\n\tfmt.Println()\n}\n"
  },
  {
    "path": "lintcmd/version/version.go",
    "content": "package version\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n)\n\nconst Version = \"devel\"\nconst MachineVersion = \"devel\"\n\n// version returns a version descriptor and reports whether the\n// version is a known release.\nfunc version(human, machine string) (human_, machine_ string, known bool) {\n\tif human != \"devel\" {\n\t\treturn human, machine, true\n\t}\n\tv, ok := buildInfoVersion()\n\tif ok {\n\t\treturn v, \"\", false\n\t}\n\treturn \"devel\", \"\", false\n}\n\nfunc Print(human, machine string) {\n\thuman, machine, release := version(human, machine)\n\n\tif release {\n\t\tfmt.Printf(\"%s %s (%s)\\n\", filepath.Base(os.Args[0]), human, machine)\n\t} else if human == \"devel\" {\n\t\tfmt.Printf(\"%s (no version)\\n\", filepath.Base(os.Args[0]))\n\t} else {\n\t\tfmt.Printf(\"%s (devel, %s)\\n\", filepath.Base(os.Args[0]), human)\n\t}\n}\n\nfunc Verbose(human, machine string) {\n\tPrint(human, machine)\n\tfmt.Println()\n\tfmt.Println(\"Compiled with Go version:\", runtime.Version())\n\tprintBuildInfo()\n}\n"
  },
  {
    "path": "pattern/convert.go",
    "content": "package pattern\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"reflect\"\n)\n\nvar astTypes = map[string]reflect.Type{\n\t\"Ellipsis\":       reflect.TypeFor[ast.Ellipsis](),\n\t\"RangeStmt\":      reflect.TypeFor[ast.RangeStmt](),\n\t\"AssignStmt\":     reflect.TypeFor[ast.AssignStmt](),\n\t\"IndexExpr\":      reflect.TypeFor[ast.IndexExpr](),\n\t\"IndexListExpr\":  reflect.TypeFor[ast.IndexListExpr](),\n\t\"Ident\":          reflect.TypeFor[ast.Ident](),\n\t\"ValueSpec\":      reflect.TypeFor[ast.ValueSpec](),\n\t\"GenDecl\":        reflect.TypeFor[ast.GenDecl](),\n\t\"BinaryExpr\":     reflect.TypeFor[ast.BinaryExpr](),\n\t\"ForStmt\":        reflect.TypeFor[ast.ForStmt](),\n\t\"ArrayType\":      reflect.TypeFor[ast.ArrayType](),\n\t\"DeferStmt\":      reflect.TypeFor[ast.DeferStmt](),\n\t\"MapType\":        reflect.TypeFor[ast.MapType](),\n\t\"ReturnStmt\":     reflect.TypeFor[ast.ReturnStmt](),\n\t\"SliceExpr\":      reflect.TypeFor[ast.SliceExpr](),\n\t\"StarExpr\":       reflect.TypeFor[ast.StarExpr](),\n\t\"UnaryExpr\":      reflect.TypeFor[ast.UnaryExpr](),\n\t\"SendStmt\":       reflect.TypeFor[ast.SendStmt](),\n\t\"SelectStmt\":     reflect.TypeFor[ast.SelectStmt](),\n\t\"ImportSpec\":     reflect.TypeFor[ast.ImportSpec](),\n\t\"IfStmt\":         reflect.TypeFor[ast.IfStmt](),\n\t\"GoStmt\":         reflect.TypeFor[ast.GoStmt](),\n\t\"Field\":          reflect.TypeFor[ast.Field](),\n\t\"SelectorExpr\":   reflect.TypeFor[ast.SelectorExpr](),\n\t\"StructType\":     reflect.TypeFor[ast.StructType](),\n\t\"KeyValueExpr\":   reflect.TypeFor[ast.KeyValueExpr](),\n\t\"FuncType\":       reflect.TypeFor[ast.FuncType](),\n\t\"FuncLit\":        reflect.TypeFor[ast.FuncLit](),\n\t\"FuncDecl\":       reflect.TypeFor[ast.FuncDecl](),\n\t\"ChanType\":       reflect.TypeFor[ast.ChanType](),\n\t\"CallExpr\":       reflect.TypeFor[ast.CallExpr](),\n\t\"CaseClause\":     reflect.TypeFor[ast.CaseClause](),\n\t\"CommClause\":     reflect.TypeFor[ast.CommClause](),\n\t\"CompositeLit\":   reflect.TypeFor[ast.CompositeLit](),\n\t\"EmptyStmt\":      reflect.TypeFor[ast.EmptyStmt](),\n\t\"SwitchStmt\":     reflect.TypeFor[ast.SwitchStmt](),\n\t\"TypeSwitchStmt\": reflect.TypeFor[ast.TypeSwitchStmt](),\n\t\"TypeAssertExpr\": reflect.TypeFor[ast.TypeAssertExpr](),\n\t\"TypeSpec\":       reflect.TypeFor[ast.TypeSpec](),\n\t\"InterfaceType\":  reflect.TypeFor[ast.InterfaceType](),\n\t\"BranchStmt\":     reflect.TypeFor[ast.BranchStmt](),\n\t\"IncDecStmt\":     reflect.TypeFor[ast.IncDecStmt](),\n\t\"BasicLit\":       reflect.TypeFor[ast.BasicLit](),\n}\n\nfunc ASTToNode(node any) Node {\n\tswitch node := node.(type) {\n\tcase *ast.File:\n\t\tpanic(\"cannot convert *ast.File to Node\")\n\tcase nil:\n\t\treturn Nil{}\n\tcase string:\n\t\treturn String(node)\n\tcase token.Token:\n\t\treturn Token(node)\n\tcase *ast.ExprStmt:\n\t\treturn ASTToNode(node.X)\n\tcase *ast.BlockStmt:\n\t\tif node == nil {\n\t\t\treturn Nil{}\n\t\t}\n\t\treturn ASTToNode(node.List)\n\tcase *ast.FieldList:\n\t\tif node == nil {\n\t\t\treturn Nil{}\n\t\t}\n\t\treturn ASTToNode(node.List)\n\tcase *ast.BasicLit:\n\t\tif node == nil {\n\t\t\treturn Nil{}\n\t\t}\n\tcase *ast.ParenExpr:\n\t\treturn ASTToNode(node.X)\n\t}\n\n\tif node, ok := node.(ast.Node); ok {\n\t\tname := reflect.TypeOf(node).Elem().Name()\n\t\tT, ok := structNodes[name]\n\t\tif !ok {\n\t\t\tpanic(fmt.Sprintf(\"internal error: unhandled type %T\", node))\n\t\t}\n\n\t\tif reflect.ValueOf(node).IsNil() {\n\t\t\treturn Nil{}\n\t\t}\n\t\tv := reflect.ValueOf(node).Elem()\n\t\tobjs := make([]Node, T.NumField())\n\t\tfor i := 0; i < T.NumField(); i++ {\n\t\t\tf := v.FieldByName(T.Field(i).Name)\n\t\t\tobjs[i] = ASTToNode(f.Interface())\n\t\t}\n\n\t\tn, err := populateNode(name, objs, false)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Sprintf(\"internal error: %s\", err))\n\t\t}\n\t\treturn n\n\t}\n\n\ts := reflect.ValueOf(node)\n\tif s.Kind() == reflect.Slice {\n\t\tif s.Len() == 0 {\n\t\t\treturn List{}\n\t\t}\n\t\tif s.Len() == 1 {\n\t\t\treturn ASTToNode(s.Index(0).Interface())\n\t\t}\n\n\t\ttail := List{}\n\t\tfor i := s.Len() - 1; i >= 0; i-- {\n\t\t\thead := ASTToNode(s.Index(i).Interface())\n\t\t\tl := List{\n\t\t\t\tHead: head,\n\t\t\t\tTail: tail,\n\t\t\t}\n\t\t\ttail = l\n\t\t}\n\t\treturn tail\n\t}\n\n\tpanic(fmt.Sprintf(\"internal error: unhandled type %T\", node))\n}\n\nfunc NodeToAST(node Node, state State) any {\n\tswitch node := node.(type) {\n\tcase Binding:\n\t\tv, ok := state[node.Name]\n\t\tif !ok {\n\t\t\t// really we want to return an error here\n\t\t\tpanic(\"XXX\")\n\t\t}\n\t\tswitch v := v.(type) {\n\t\tcase types.Object:\n\t\t\treturn &ast.Ident{Name: v.Name()}\n\t\tdefault:\n\t\t\treturn v\n\t\t}\n\tcase Builtin, Any, Object, Symbol, Not, Or:\n\t\tpanic(\"XXX\")\n\tcase List:\n\t\tif (node == List{}) {\n\t\t\treturn []ast.Node{}\n\t\t}\n\t\tx := []ast.Node{NodeToAST(node.Head, state).(ast.Node)}\n\t\tx = append(x, NodeToAST(node.Tail, state).([]ast.Node)...)\n\t\treturn x\n\tcase Token:\n\t\treturn token.Token(node)\n\tcase String:\n\t\treturn string(node)\n\tcase Nil:\n\t\treturn nil\n\t}\n\n\tname := reflect.TypeOf(node).Name()\n\tT, ok := astTypes[name]\n\tif !ok {\n\t\tpanic(fmt.Sprintf(\"internal error: unhandled type %T\", node))\n\t}\n\tv := reflect.ValueOf(node)\n\tout := reflect.New(T)\n\tfor i := 0; i < T.NumField(); i++ {\n\t\tfNode := v.FieldByName(T.Field(i).Name)\n\t\tif (fNode == reflect.Value{}) {\n\t\t\tcontinue\n\t\t}\n\t\tfAST := out.Elem().FieldByName(T.Field(i).Name)\n\t\tswitch fAST.Type().Kind() {\n\t\tcase reflect.Slice:\n\t\t\tc := reflect.ValueOf(NodeToAST(fNode.Interface().(Node), state))\n\t\t\tif c.Kind() != reflect.Slice {\n\t\t\t\t// it's a single node in the pattern, we have to wrap\n\t\t\t\t// it in a slice\n\t\t\t\tslice := reflect.MakeSlice(fAST.Type(), 1, 1)\n\t\t\t\tslice.Index(0).Set(c)\n\t\t\t\tc = slice\n\t\t\t}\n\t\t\tswitch fAST.Interface().(type) {\n\t\t\tcase []ast.Node:\n\t\t\t\tswitch cc := c.Interface().(type) {\n\t\t\t\tcase []ast.Node:\n\t\t\t\t\tfAST.Set(c)\n\t\t\t\tcase []ast.Expr:\n\t\t\t\t\tvar slice []ast.Node\n\t\t\t\t\tfor _, el := range cc {\n\t\t\t\t\t\tslice = append(slice, el)\n\t\t\t\t\t}\n\t\t\t\t\tfAST.Set(reflect.ValueOf(slice))\n\t\t\t\tdefault:\n\t\t\t\t\tpanic(\"XXX\")\n\t\t\t\t}\n\t\t\tcase []ast.Expr:\n\t\t\t\tswitch cc := c.Interface().(type) {\n\t\t\t\tcase []ast.Node:\n\t\t\t\t\tvar slice []ast.Expr\n\t\t\t\t\tfor _, el := range cc {\n\t\t\t\t\t\tslice = append(slice, el.(ast.Expr))\n\t\t\t\t\t}\n\t\t\t\t\tfAST.Set(reflect.ValueOf(slice))\n\t\t\t\tcase []ast.Expr:\n\t\t\t\t\tfAST.Set(c)\n\t\t\t\tdefault:\n\t\t\t\t\tpanic(\"XXX\")\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tpanic(\"XXX\")\n\t\t\t}\n\t\tcase reflect.Int:\n\t\t\tc := reflect.ValueOf(NodeToAST(fNode.Interface().(Node), state))\n\t\t\tswitch c.Kind() {\n\t\t\tcase reflect.String:\n\t\t\t\ttok, ok := tokensByString[c.Interface().(string)]\n\t\t\t\tif !ok {\n\t\t\t\t\t// really we want to return an error here\n\t\t\t\t\tpanic(\"XXX\")\n\t\t\t\t}\n\t\t\t\tfAST.SetInt(int64(tok))\n\t\t\tcase reflect.Int:\n\t\t\t\tfAST.Set(c)\n\t\t\tdefault:\n\t\t\t\tpanic(fmt.Sprintf(\"internal error: unexpected kind %s\", c.Kind()))\n\t\t\t}\n\t\tdefault:\n\t\t\tr := NodeToAST(fNode.Interface().(Node), state)\n\t\t\tif r != nil {\n\t\t\t\tfAST.Set(reflect.ValueOf(r))\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out.Interface().(ast.Node)\n}\n"
  },
  {
    "path": "pattern/doc.go",
    "content": "/*\nPackage pattern implements a simple language for pattern matching Go ASTs.\n\n# Design decisions and trade-offs\n\nThe language is designed specifically for the task of filtering ASTs\nto simplify the implementation of analyses in staticcheck.\nIt is also intended to be trivial to parse and execute.\n\nTo that end, we make certain decisions that make the language more\nsuited to its task, while making certain queries infeasible.\n\nFurthermore, it is fully expected that the majority of analyses will still require ordinary Go code\nto further process the filtered AST, to make use of type information and to enforce complex invariants.\nIt is not our goal to design a scripting language for writing entire checks in.\n\n# The language\n\nAt its core, patterns are a representation of Go ASTs, allowing for the use of placeholders to enable pattern matching.\nTheir syntax is inspired by LISP and Haskell, but unlike LISP, the core unit of patterns isn't the list, but the node.\nThere is a fixed set of nodes, identified by name, and with the exception of the Or node, all nodes have a fixed number of arguments.\nIn addition to nodes, there are atoms, which represent basic units such as strings or the nil value.\n\nPattern matching is implemented via bindings, represented by the Binding node.\nA Binding can match nodes and associate them with names, to later recall the nodes.\nThis allows for expressing \"this node must be equal to that node\" constraints.\n\nTo simplify writing and reading patterns, a small amount of additional syntax exists on top of nodes and atoms.\nThis additional syntax doesn't add any new features of its own, it simply provides shortcuts to creating nodes and atoms.\n\nTo show an example of a pattern, first consider this snippet of Go code:\n\n\tif x := fn(); x != nil {\n\t\tfor _, v := range x {\n\t\t\tprintln(v, x)\n\t\t}\n\t}\n\nThe corresponding AST expressed as an idiomatic pattern would look as follows:\n\n\t(IfStmt\n\t\t(AssignStmt (Ident \"x\") \":=\" (CallExpr (Ident \"fn\") []))\n\t\t(BinaryExpr (Ident \"x\") \"!=\" (Ident \"nil\"))\n\t\t(RangeStmt\n\t\t\t(Ident \"_\") (Ident \"v\") \":=\" (Ident \"x\")\n\t\t\t(CallExpr (Ident \"println\") [(Ident \"v\") (Ident \"x\")]))\n\t\tnil)\n\nTwo things are worth noting about this representation.\nFirst, the [el1 el2 ...] syntax is a short-hand for creating lists.\nIt is a short-hand for el1:el2:[], which itself is a short-hand for (List el1 (List el2 (List nil nil)).\nSecond, note the absence of a lot of lists in places that normally accept lists.\nFor example, assignment assigns a number of right-hands to a number of left-hands, yet our AssignStmt is lacking any form of list.\nThis is due to the fact that a single node can match a list of exactly one element.\nThus, the two following forms have identical matching behavior:\n\n\t(AssignStmt (Ident \"x\") \":=\" (CallExpr (Ident \"fn\") []))\n\t(AssignStmt [(Ident \"x\")] \":=\" [(CallExpr (Ident \"fn\") [])])\n\nThis section serves as an overview of the language's syntax.\nMore in-depth explanations of the matching behavior as well as an exhaustive list of node types follows in the coming sections.\n\n# Pattern matching\n\n# TODO write about pattern matching\n\n- inspired by haskell syntax, but much, much simpler and naive\n\n# Node types\n\nThe language contains two kinds of nodes: those that map to nodes in the AST, and those that implement additional logic.\n\nNodes that map directly to AST nodes are named identically to the types in the go/ast package.\nWhat follows is an exhaustive list of these nodes:\n\n\t(ArrayType len elt)\n\t(AssignStmt lhs tok rhs)\n\t(BasicLit kind value)\n\t(BinaryExpr x op y)\n\t(BranchStmt tok label)\n\t(CallExpr fun args)\n\t(CaseClause list body)\n\t(ChanType dir value)\n\t(CommClause comm body)\n\t(CompositeLit type elts)\n\t(DeferStmt call)\n\t(Ellipsis elt)\n\t(EmptyStmt)\n\t(Field names type tag)\n\t(ForStmt init cond post body)\n\t(FuncDecl recv name type body)\n\t(FuncLit type body)\n\t(FuncType params results)\n\t(GenDecl specs)\n\t(GoStmt call)\n\t(Ident name)\n\t(IfStmt init cond body else)\n\t(ImportSpec name path)\n\t(IncDecStmt x tok)\n\t(IndexExpr x index)\n\t(InterfaceType methods)\n\t(KeyValueExpr key value)\n\t(MapType key value)\n\t(RangeStmt key value tok x body)\n\t(ReturnStmt results)\n\t(SelectStmt body)\n\t(SelectorExpr x sel)\n\t(SendStmt chan value)\n\t(SliceExpr x low high max)\n\t(StarExpr x)\n\t(StructType fields)\n\t(SwitchStmt init tag body)\n\t(TypeAssertExpr)\n\t(TypeSpec name type)\n\t(TypeSwitchStmt init assign body)\n\t(UnaryExpr op x)\n\t(ValueSpec names type values)\n\nAdditionally, there are the String, Token and nil atoms.\nStrings are double-quoted string literals, as in (Ident \"someName\").\nTokens are also represented as double-quoted string literals, but are converted to token.Token values in contexts that require tokens,\nsuch as in (BinaryExpr x \"<\" y), where \"<\" is transparently converted to token.LSS during matching.\nThe keyword 'nil' denotes the nil value, which represents the absence of any value.\n\nWe also define the (List head tail) node, which is used to represent sequences of elements as a singly linked list.\nThe head is a single element, and the tail is the remainder of the list.\nFor example,\n\n\t(List \"foo\" (List \"bar\" (List \"baz\" (List nil nil))))\n\nrepresents a list of three elements, \"foo\", \"bar\" and \"baz\". There is dedicated syntax for writing lists, which looks as follows:\n\n\t[\"foo\" \"bar\" \"baz\"]\n\nThis syntax is itself syntactic sugar for the following form:\n\n\t\"foo\":\"bar\":\"baz\":[]\n\nThis form is of particular interest for pattern matching, as it allows matching on the head and tail. For example,\n\n\t\"foo\":\"bar\":_\n\nwould match any list with at least two elements, where the first two elements are \"foo\" and \"bar\". This is equivalent to writing\n\n\t(List \"foo\" (List \"bar\" _))\n\nNote that it is not possible to match from the end of the list.\nThat is, there is no way to express a query such as \"a list of any length where the last element is foo\".\n\nNote that unlike in LISP, nil and empty lists are distinct from one another.\nIn patterns, with respect to lists, nil is akin to Go's untyped nil.\nIt will match a nil ast.Node, but it will not match a nil []ast.Expr. Nil will, however, match pointers to named types such as *ast.Ident.\nSimilarly, lists are akin to Go's\nslices. An empty list will match both a nil and an empty []ast.Expr, but it will not match a nil ast.Node.\n\nDue to the difference between nil and empty lists, an empty list is represented as (List nil nil), i.e. a list with no head or tail.\nSimilarly, a list of one element is represented as (List el (List nil nil)). Unlike in LISP, it cannot be represented by (List el nil).\n\nFinally, there are nodes that implement special logic or matching behavior.\n\n(Any) matches any value. The underscore (_) maps to this node, making the following two forms equivalent:\n\n\t(Ident _)\n\t(Ident (Any))\n\n(Builtin name) matches a built-in identifier or function by name.\nThis is a type-aware variant of (Ident name).\nInstead of only comparing the name, it resolves the object behind the name and makes sure it's a pre-declared identifier.\n\nFor example, in the following piece of code\n\n\tfunc fn() {\n\t\tprintln(true)\n\t\ttrue := false\n\t\tprintln(true)\n\t}\n\nthe pattern\n\n\t(Builtin \"true\")\n\nwill match exactly once, on the first use of 'true' in the function.\nSubsequent occurrences of 'true' no longer refer to the pre-declared identifier.\n\n(Object name) matches an identifier by name, but yields the\ntypes.Object it refers to.\n\n(Symbol name) matches ast.Idents and ast.SelectorExprs that refer to a symbol with a given fully qualified name.\nFor example, \"net/url.PathEscape\" matches the PathEscape function in the net/url package,\nand \"(net/url.EscapeError).Error\" refers to the Error method on the net/url.EscapeError type,\neither on an instance of the type, or on the type itself.\n\nFor example, the following patterns match the following lines of code:\n\n\t(CallExpr (Symbol \"fmt.Println\") _) // pattern 1\n\t(CallExpr (Symbol \"(net/url.EscapeError).Error\") _) // pattern 2\n\n\tfmt.Println(\"hello, world\") // matches pattern 1\n\tvar x url.EscapeError\n\tx.Error() // matches pattern 2\n\t(url.EscapeError).Error(x) // also matches pattern 2\n\n(Binding name node) creates or uses a binding.\nBindings work like variable assignments, allowing referring to already matched nodes.\nAs an example, bindings are necessary to match self-assignment of the form \"x = x\",\nsince we need to express that the right-hand side is identical to the left-hand side.\n\nIf a binding's node is not nil, the matcher will attempt to match a node according to the pattern.\nIf a binding's node is nil, the binding will either recall an existing value, or match the Any node.\nIt is an error to provide a non-nil node to a binding that has already been bound.\n\nReferring back to the earlier example, the following pattern will match self-assignment of idents:\n\n\t(AssignStmt (Binding \"lhs\" (Ident _)) \"=\" (Binding \"lhs\" nil))\n\nBecause bindings are a crucial component of pattern matching, there is special syntax for creating and recalling bindings.\nLower-case names refer to bindings. If standing on its own, the name \"foo\" will be equivalent to (Binding \"foo\" nil).\nIf a name is followed by an at-sign (@) then it will create a binding for the node that follows.\nTogether, this allows us to rewrite the earlier example as follows:\n\n\t(AssignStmt lhs@(Ident _) \"=\" lhs)\n\n(Or nodes...) is a variadic node that tries matching each node until one succeeds. For example, the following pattern matches all idents of name \"foo\" or \"bar\":\n\n\t(Ident (Or \"foo\" \"bar\"))\n\nWe could also have written\n\n\t(Or (Ident \"foo\") (Ident \"bar\"))\n\nand achieved the same result. We can also mix different kinds of nodes:\n\n\t(Or (Ident \"foo\") (CallExpr (Ident \"bar\") _))\n\nWhen using bindings inside of nodes used inside Or, all or none of the bindings will be bound.\nThat is, partially matched nodes that ultimately failed to match will not produce any bindings observable outside of the matching attempt.\nWe can thus write\n\n\t(Or (Ident name) (CallExpr name))\n\nand 'name' will either be a String if the first option matched, or an Ident or SelectorExpr if the second option matched.\n\n(Not node)\n\nThe Not node negates a match. For example, (Not (Ident _)) will match all nodes that aren't identifiers.\n\nChanDir(0)\n\n# Automatic unnesting of AST nodes\n\nThe Go AST has several types of nodes that wrap other nodes.\nTo simplify matching, we automatically unwrap some of these nodes.\n\nThese nodes are ExprStmt (for using expressions in a statement context),\nParenExpr (for parenthesized expressions),\nDeclStmt (for declarations in a statement context),\nand LabeledStmt (for labeled statements).\n\nThus, the query\n\n\t(FuncLit _ [(CallExpr _ _)]\n\nwill match a function literal containing a single function call,\neven though in the actual Go AST, the CallExpr is nested inside an ExprStmt,\nas function bodies are made up of sequences of statements.\n\nOn the flip-side, there is no way to specifically match these wrapper nodes.\nFor example, there is no way of searching for unnecessary parentheses, like in the following piece of Go code:\n\n\t((x)) += 2\n*/\npackage pattern\n"
  },
  {
    "path": "pattern/lexer.go",
    "content": "package pattern\n\nimport (\n\t\"fmt\"\n\t\"go/token\"\n\t\"iter\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n)\n\n// lex returns the sequence of tokens in the input.\nfunc lex(f *token.File, input string) iter.Seq[item] {\n\treturn func(yield func(item) bool) {\n\t\tlex := &lexer{\n\t\t\tf:     f,\n\t\t\tinput: input,\n\t\t\tyield: yield,\n\t\t}\n\t\tlex.run()\n\t}\n}\n\n// lexer holds the state of a single [lex] iteration.\ntype lexer struct {\n\tf *token.File\n\n\tinput string\n\tstart int\n\tpos   int\n\twidth int\n\n\tyield func(item) bool\n}\n\ntype itemType int\n\nconst eof = -1\n\nconst (\n\titemError itemType = iota\n\titemLeftParen\n\titemRightParen\n\titemLeftBracket\n\titemRightBracket\n\titemTypeName\n\titemVariable\n\titemAt\n\titemColon\n\titemBlank\n\titemString\n\titemEOF\n)\n\nfunc (typ itemType) String() string {\n\tswitch typ {\n\tcase itemError:\n\t\treturn \"ERROR\"\n\tcase itemLeftParen:\n\t\treturn \"(\"\n\tcase itemRightParen:\n\t\treturn \")\"\n\tcase itemLeftBracket:\n\t\treturn \"[\"\n\tcase itemRightBracket:\n\t\treturn \"]\"\n\tcase itemTypeName:\n\t\treturn \"TYPE\"\n\tcase itemVariable:\n\t\treturn \"VAR\"\n\tcase itemAt:\n\t\treturn \"@\"\n\tcase itemColon:\n\t\treturn \":\"\n\tcase itemBlank:\n\t\treturn \"_\"\n\tcase itemString:\n\t\treturn \"STRING\"\n\tcase itemEOF:\n\t\treturn \"EOF\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"itemType(%d)\", typ)\n\t}\n}\n\ntype item struct {\n\ttyp itemType\n\tval string\n\tpos int\n}\n\ntype stateFn func(*lexer) stateFn\n\nfunc (l *lexer) run() {\n\tfor state := lexStart; state != nil; {\n\t\tstate = state(l)\n\t}\n}\n\nfunc (l *lexer) emitValue(t itemType, value string) bool {\n\tok := l.yield(item{t, value, l.start})\n\tl.start = l.pos\n\treturn ok\n}\n\nfunc (l *lexer) emit(t itemType) bool {\n\tok := l.yield(item{t, l.input[l.start:l.pos], l.start})\n\tl.start = l.pos\n\treturn ok\n}\n\nfunc lexStart(l *lexer) stateFn {\n\tswitch r := l.next(); {\n\tcase r == eof:\n\t\t_ = l.emit(itemEOF)\n\t\treturn nil\n\tcase unicode.IsSpace(r):\n\t\tl.ignore()\n\tcase r == '(':\n\t\tif !l.emit(itemLeftParen) {\n\t\t\treturn nil\n\t\t}\n\tcase r == ')':\n\t\tif !l.emit(itemRightParen) {\n\t\t\treturn nil\n\t\t}\n\tcase r == '[':\n\t\tif !l.emit(itemLeftBracket) {\n\t\t\treturn nil\n\t\t}\n\tcase r == ']':\n\t\tif !l.emit(itemRightBracket) {\n\t\t\treturn nil\n\t\t}\n\tcase r == '@':\n\t\tif !l.emit(itemAt) {\n\t\t\treturn nil\n\t\t}\n\tcase r == ':':\n\t\tif !l.emit(itemColon) {\n\t\t\treturn nil\n\t\t}\n\tcase r == '_':\n\t\tif !l.emit(itemBlank) {\n\t\t\treturn nil\n\t\t}\n\tcase r == '\"':\n\t\tl.backup()\n\t\treturn lexString\n\tcase unicode.IsUpper(r):\n\t\tl.backup()\n\t\treturn lexType\n\tcase unicode.IsLower(r):\n\t\tl.backup()\n\t\treturn lexVariable\n\tdefault:\n\t\treturn l.errorf(\"unexpected character %c\", r)\n\t}\n\treturn lexStart\n}\n\nfunc (l *lexer) next() (r rune) {\n\tif l.pos >= len(l.input) {\n\t\tl.width = 0\n\t\treturn eof\n\t}\n\tr, l.width = utf8.DecodeRuneInString(l.input[l.pos:])\n\n\tif r == '\\n' {\n\t\tl.f.AddLine(l.pos)\n\t}\n\n\tl.pos += l.width\n\n\treturn r\n}\n\nfunc (l *lexer) ignore() {\n\tl.start = l.pos\n}\n\nfunc (l *lexer) backup() {\n\tl.pos -= l.width\n}\n\nfunc (l *lexer) errorf(format string, args ...any) stateFn {\n\t// TODO(dh): emit position information in errors\n\t_ = l.yield(item{\n\t\titemError,\n\t\tfmt.Sprintf(format, args...),\n\t\tl.start,\n\t})\n\treturn nil\n}\n\nfunc isAlphaNumeric(r rune) bool {\n\treturn r >= '0' && r <= '9' ||\n\t\tr >= 'a' && r <= 'z' ||\n\t\tr >= 'A' && r <= 'Z'\n}\n\nfunc lexString(l *lexer) stateFn {\n\tl.next() // skip quote\n\tescape := false\n\n\tvar runes []rune\n\tfor {\n\t\tswitch r := l.next(); r {\n\t\tcase eof:\n\t\t\treturn l.errorf(\"unterminated string\")\n\t\tcase '\"':\n\t\t\tif !escape {\n\t\t\t\tif !l.emitValue(itemString, string(runes)) {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn lexStart\n\t\t\t} else {\n\t\t\t\trunes = append(runes, '\"')\n\t\t\t\tescape = false\n\t\t\t}\n\t\tcase '\\\\':\n\t\t\tif escape {\n\t\t\t\trunes = append(runes, '\\\\')\n\t\t\t\tescape = false\n\t\t\t} else {\n\t\t\t\tescape = true\n\t\t\t}\n\t\tdefault:\n\t\t\trunes = append(runes, r)\n\t\t}\n\t}\n}\n\nfunc lexType(l *lexer) stateFn {\n\tl.next()\n\tfor {\n\t\tif !isAlphaNumeric(l.next()) {\n\t\t\tl.backup()\n\t\t\tif !l.emit(itemTypeName) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn lexStart\n\t\t}\n\t}\n}\n\nfunc lexVariable(l *lexer) stateFn {\n\tl.next()\n\tfor {\n\t\tif !isAlphaNumeric(l.next()) {\n\t\t\tl.backup()\n\t\t\tif !l.emit(itemVariable) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn lexStart\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pattern/match.go",
    "content": "package pattern\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"reflect\"\n)\n\nvar tokensByString = map[string]Token{\n\t\"INT\":         Token(token.INT),\n\t\"FLOAT\":       Token(token.FLOAT),\n\t\"IMAG\":        Token(token.IMAG),\n\t\"CHAR\":        Token(token.CHAR),\n\t\"STRING\":      Token(token.STRING),\n\t\"+\":           Token(token.ADD),\n\t\"-\":           Token(token.SUB),\n\t\"*\":           Token(token.MUL),\n\t\"/\":           Token(token.QUO),\n\t\"%\":           Token(token.REM),\n\t\"&\":           Token(token.AND),\n\t\"|\":           Token(token.OR),\n\t\"^\":           Token(token.XOR),\n\t\"<<\":          Token(token.SHL),\n\t\">>\":          Token(token.SHR),\n\t\"&^\":          Token(token.AND_NOT),\n\t\"+=\":          Token(token.ADD_ASSIGN),\n\t\"-=\":          Token(token.SUB_ASSIGN),\n\t\"*=\":          Token(token.MUL_ASSIGN),\n\t\"/=\":          Token(token.QUO_ASSIGN),\n\t\"%=\":          Token(token.REM_ASSIGN),\n\t\"&=\":          Token(token.AND_ASSIGN),\n\t\"|=\":          Token(token.OR_ASSIGN),\n\t\"^=\":          Token(token.XOR_ASSIGN),\n\t\"<<=\":         Token(token.SHL_ASSIGN),\n\t\">>=\":         Token(token.SHR_ASSIGN),\n\t\"&^=\":         Token(token.AND_NOT_ASSIGN),\n\t\"&&\":          Token(token.LAND),\n\t\"||\":          Token(token.LOR),\n\t\"<-\":          Token(token.ARROW),\n\t\"++\":          Token(token.INC),\n\t\"--\":          Token(token.DEC),\n\t\"==\":          Token(token.EQL),\n\t\"<\":           Token(token.LSS),\n\t\">\":           Token(token.GTR),\n\t\"=\":           Token(token.ASSIGN),\n\t\"!\":           Token(token.NOT),\n\t\"!=\":          Token(token.NEQ),\n\t\"<=\":          Token(token.LEQ),\n\t\">=\":          Token(token.GEQ),\n\t\":=\":          Token(token.DEFINE),\n\t\"...\":         Token(token.ELLIPSIS),\n\t\"IMPORT\":      Token(token.IMPORT),\n\t\"VAR\":         Token(token.VAR),\n\t\"TYPE\":        Token(token.TYPE),\n\t\"CONST\":       Token(token.CONST),\n\t\"BREAK\":       Token(token.BREAK),\n\t\"CONTINUE\":    Token(token.CONTINUE),\n\t\"GOTO\":        Token(token.GOTO),\n\t\"FALLTHROUGH\": Token(token.FALLTHROUGH),\n}\n\nfunc maybeToken(node Node) (Node, bool) {\n\tif node, ok := node.(String); ok {\n\t\tif tok, ok := tokensByString[string(node)]; ok {\n\t\t\treturn tok, true\n\t\t}\n\t\treturn node, false\n\t}\n\treturn node, false\n}\n\nfunc isNil(v any) bool {\n\tif v == nil {\n\t\treturn true\n\t}\n\tif _, ok := v.(Nil); ok {\n\t\treturn true\n\t}\n\treturn false\n}\n\ntype matcher interface {\n\tMatch(*Matcher, any) (any, bool)\n}\n\ntype State = map[string]any\n\ntype Matcher struct {\n\tTypesInfo *types.Info\n\tState     State\n\n\tbindingsMapping []string\n\n\tsetBindings []uint64\n}\n\nfunc (m *Matcher) set(b Binding, value any) {\n\tm.State[b.Name] = value\n\tm.setBindings[len(m.setBindings)-1] |= 1 << b.idx\n}\n\nfunc (m *Matcher) push() {\n\tm.setBindings = append(m.setBindings, 0)\n}\n\nfunc (m *Matcher) pop() {\n\tset := m.setBindings[len(m.setBindings)-1]\n\tif set != 0 {\n\t\tfor i := 0; i < len(m.bindingsMapping); i++ {\n\t\t\tif (set & (1 << i)) != 0 {\n\t\t\t\tkey := m.bindingsMapping[i]\n\t\t\t\tdelete(m.State, key)\n\t\t\t}\n\t\t}\n\t}\n\tm.setBindings = m.setBindings[:len(m.setBindings)-1]\n}\n\nfunc (m *Matcher) merge() {\n\tm.setBindings = m.setBindings[:len(m.setBindings)-1]\n}\n\nfunc (m *Matcher) Match(a Pattern, b ast.Node) bool {\n\tm.bindingsMapping = a.Bindings\n\tm.State = State{}\n\tm.push()\n\t_, ok := match(m, a.Root, b)\n\tm.merge()\n\tif len(m.setBindings) != 0 {\n\t\tpanic(fmt.Sprintf(\"%d entries left on the stack, expected none\", len(m.setBindings)))\n\t}\n\treturn ok\n}\n\nfunc Match(a Pattern, b ast.Node) (*Matcher, bool) {\n\tm := &Matcher{}\n\tret := m.Match(a, b)\n\treturn m, ret\n}\n\n// Match two items, which may be (Node, AST) or (AST, AST)\nfunc match(m *Matcher, l, r any) (any, bool) {\n\tif _, ok := r.(Node); ok {\n\t\tpanic(\"Node mustn't be on right side of match\")\n\t}\n\n\tswitch l := l.(type) {\n\tcase *ast.ParenExpr:\n\t\treturn match(m, l.X, r)\n\tcase *ast.ExprStmt:\n\t\treturn match(m, l.X, r)\n\tcase *ast.DeclStmt:\n\t\treturn match(m, l.Decl, r)\n\tcase *ast.LabeledStmt:\n\t\treturn match(m, l.Stmt, r)\n\tcase *ast.BlockStmt:\n\t\treturn match(m, l.List, r)\n\tcase *ast.FieldList:\n\t\tif l == nil {\n\t\t\treturn match(m, nil, r)\n\t\t} else {\n\t\t\treturn match(m, l.List, r)\n\t\t}\n\t}\n\n\tswitch r := r.(type) {\n\tcase *ast.ParenExpr:\n\t\treturn match(m, l, r.X)\n\tcase *ast.ExprStmt:\n\t\treturn match(m, l, r.X)\n\tcase *ast.DeclStmt:\n\t\treturn match(m, l, r.Decl)\n\tcase *ast.LabeledStmt:\n\t\treturn match(m, l, r.Stmt)\n\tcase *ast.BlockStmt:\n\t\tif r == nil {\n\t\t\treturn match(m, l, nil)\n\t\t}\n\t\treturn match(m, l, r.List)\n\tcase *ast.FieldList:\n\t\tif r == nil {\n\t\t\treturn match(m, l, nil)\n\t\t}\n\t\treturn match(m, l, r.List)\n\tcase *ast.BasicLit:\n\t\tif r == nil {\n\t\t\treturn match(m, l, nil)\n\t\t}\n\t}\n\n\tif l, ok := l.(matcher); ok {\n\t\treturn l.Match(m, r)\n\t}\n\n\tif l, ok := l.(Node); ok {\n\t\t// Matching of pattern with concrete value\n\t\treturn matchNodeAST(m, l, r)\n\t}\n\n\tif l == nil || r == nil {\n\t\treturn nil, l == r\n\t}\n\n\t{\n\t\tln, ok1 := l.(ast.Node)\n\t\trn, ok2 := r.(ast.Node)\n\t\tif ok1 && ok2 {\n\t\t\treturn matchAST(m, ln, rn)\n\t\t}\n\t}\n\n\t{\n\t\tobj, ok := l.(types.Object)\n\t\tif ok {\n\t\t\tswitch r := r.(type) {\n\t\t\tcase *ast.Ident:\n\t\t\t\treturn obj, obj == m.TypesInfo.ObjectOf(r)\n\t\t\tcase *ast.SelectorExpr:\n\t\t\t\treturn obj, obj == m.TypesInfo.ObjectOf(r.Sel)\n\t\t\tdefault:\n\t\t\t\treturn obj, false\n\t\t\t}\n\t\t}\n\t}\n\n\t// TODO(dh): the three blocks handling slices can be combined into a single block if we use reflection\n\n\t{\n\t\tln, ok1 := l.([]ast.Expr)\n\t\trn, ok2 := r.([]ast.Expr)\n\t\tif ok1 || ok2 {\n\t\t\tif ok1 && !ok2 {\n\t\t\t\tcast, ok := r.(ast.Expr)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\t\t\t\trn = []ast.Expr{cast}\n\t\t\t} else if !ok1 && ok2 {\n\t\t\t\tcast, ok := l.(ast.Expr)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\t\t\t\tln = []ast.Expr{cast}\n\t\t\t}\n\n\t\t\tif len(ln) != len(rn) {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t\tfor i, ll := range ln {\n\t\t\t\tif _, ok := match(m, ll, rn[i]); !ok {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn r, true\n\t\t}\n\t}\n\n\t{\n\t\tln, ok1 := l.([]ast.Stmt)\n\t\trn, ok2 := r.([]ast.Stmt)\n\t\tif ok1 || ok2 {\n\t\t\tif ok1 && !ok2 {\n\t\t\t\tcast, ok := r.(ast.Stmt)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\t\t\t\trn = []ast.Stmt{cast}\n\t\t\t} else if !ok1 && ok2 {\n\t\t\t\tcast, ok := l.(ast.Stmt)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\t\t\t\tln = []ast.Stmt{cast}\n\t\t\t}\n\n\t\t\tif len(ln) != len(rn) {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t\tfor i, ll := range ln {\n\t\t\t\tif _, ok := match(m, ll, rn[i]); !ok {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn r, true\n\t\t}\n\t}\n\n\t{\n\t\tln, ok1 := l.([]*ast.Field)\n\t\trn, ok2 := r.([]*ast.Field)\n\t\tif ok1 || ok2 {\n\t\t\tif ok1 && !ok2 {\n\t\t\t\tcast, ok := r.(*ast.Field)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\t\t\t\trn = []*ast.Field{cast}\n\t\t\t} else if !ok1 && ok2 {\n\t\t\t\tcast, ok := l.(*ast.Field)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\t\t\t\tln = []*ast.Field{cast}\n\t\t\t}\n\n\t\t\tif len(ln) != len(rn) {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t\tfor i, ll := range ln {\n\t\t\t\tif _, ok := match(m, ll, rn[i]); !ok {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn r, true\n\t\t}\n\t}\n\n\treturn nil, false\n}\n\n// Match a Node with an AST node\nfunc matchNodeAST(m *Matcher, a Node, b any) (any, bool) {\n\tswitch b := b.(type) {\n\tcase []ast.Stmt:\n\t\t// 'a' is not a List or we'd be using its Match\n\t\t// implementation.\n\n\t\tif len(b) != 1 {\n\t\t\treturn nil, false\n\t\t}\n\t\treturn match(m, a, b[0])\n\tcase []ast.Expr:\n\t\t// 'a' is not a List or we'd be using its Match\n\t\t// implementation.\n\n\t\tif len(b) != 1 {\n\t\t\treturn nil, false\n\t\t}\n\t\treturn match(m, a, b[0])\n\tcase []*ast.Field:\n\t\t// 'a' is not a List or we'd be using its Match\n\t\t// implementation\n\t\tif len(b) != 1 {\n\t\t\treturn nil, false\n\t\t}\n\t\treturn match(m, a, b[0])\n\tcase ast.Node:\n\t\tra := reflect.ValueOf(a)\n\t\trb := reflect.ValueOf(b).Elem()\n\n\t\tif ra.Type().Name() != rb.Type().Name() {\n\t\t\treturn nil, false\n\t\t}\n\n\t\tfor i := 0; i < ra.NumField(); i++ {\n\t\t\taf := ra.Field(i)\n\t\t\tfieldName := ra.Type().Field(i).Name\n\t\t\tbf := rb.FieldByName(fieldName)\n\t\t\tif (bf == reflect.Value{}) {\n\t\t\t\tpanic(fmt.Sprintf(\"internal error: could not find field %s in type %t when comparing with %T\", fieldName, b, a))\n\t\t\t}\n\t\t\tai := af.Interface()\n\t\t\tbi := bf.Interface()\n\t\t\tif ai == nil {\n\t\t\t\treturn b, bi == nil\n\t\t\t}\n\t\t\tif _, ok := match(m, ai.(Node), bi); !ok {\n\t\t\t\treturn b, false\n\t\t\t}\n\t\t}\n\t\treturn b, true\n\tcase nil:\n\t\treturn nil, a == Nil{}\n\tcase string, token.Token:\n\t\t// 'a' can't be a String, Token, or Binding or we'd be using their Match implementations.\n\t\treturn nil, false\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unhandled type %T\", b))\n\t}\n}\n\n// Match two AST nodes\nfunc matchAST(m *Matcher, a, b ast.Node) (any, bool) {\n\tra := reflect.ValueOf(a)\n\trb := reflect.ValueOf(b)\n\n\tif ra.Type() != rb.Type() {\n\t\treturn nil, false\n\t}\n\tif ra.IsNil() || rb.IsNil() {\n\t\treturn rb, ra.IsNil() == rb.IsNil()\n\t}\n\n\tra = ra.Elem()\n\trb = rb.Elem()\n\tfor i := 0; i < ra.NumField(); i++ {\n\t\taf := ra.Field(i)\n\t\tbf := rb.Field(i)\n\t\tif af.Type() == rtTokPos || af.Type() == rtObject || af.Type() == rtCommentGroup {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch af.Kind() {\n\t\tcase reflect.Slice:\n\t\t\tif af.Len() != bf.Len() {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t\tfor j := 0; j < af.Len(); j++ {\n\t\t\t\tif _, ok := match(m, af.Index(j).Interface().(ast.Node), bf.Index(j).Interface().(ast.Node)); !ok {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\t\t\t}\n\t\tcase reflect.String:\n\t\t\tif af.String() != bf.String() {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\tcase reflect.Int:\n\t\t\tif af.Int() != bf.Int() {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\tcase reflect.Bool:\n\t\t\tif af.Bool() != bf.Bool() {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\tcase reflect.Pointer, reflect.Interface:\n\t\t\tif _, ok := match(m, af.Interface(), bf.Interface()); !ok {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"internal error: unhandled kind %s (%T)\", af.Kind(), af.Interface()))\n\t\t}\n\t}\n\treturn b, true\n}\n\nfunc (b Binding) Match(m *Matcher, node any) (any, bool) {\n\tif isNil(b.Node) {\n\t\tv, ok := m.State[b.Name]\n\t\tif ok {\n\t\t\t// Recall value\n\t\t\treturn match(m, v, node)\n\t\t}\n\t\t// Matching anything\n\t\tb.Node = Any{}\n\t}\n\n\t// Store value\n\tif _, ok := m.State[b.Name]; ok {\n\t\tpanic(fmt.Sprintf(\"binding already created: %s\", b.Name))\n\t}\n\tnew, ret := match(m, b.Node, node)\n\tif ret {\n\t\tm.set(b, new)\n\t}\n\treturn new, ret\n}\n\nfunc (Any) Match(m *Matcher, node any) (any, bool) {\n\treturn node, true\n}\n\nfunc (l List) Match(m *Matcher, node any) (any, bool) {\n\tv := reflect.ValueOf(node)\n\tif v.Kind() == reflect.Slice {\n\t\tif isNil(l.Head) {\n\t\t\treturn node, v.Len() == 0\n\t\t}\n\t\tif v.Len() == 0 {\n\t\t\treturn nil, false\n\t\t}\n\t\t// OPT(dh): don't check the entire tail if head didn't match\n\t\t_, ok1 := match(m, l.Head, v.Index(0).Interface())\n\t\t_, ok2 := match(m, l.Tail, v.Slice(1, v.Len()).Interface())\n\t\treturn node, ok1 && ok2\n\t}\n\t// Our empty list does not equal an untyped Go nil. This way, we can\n\t// tell apart an if with no else and an if with an empty else.\n\treturn nil, false\n}\n\nfunc (s String) Match(m *Matcher, node any) (any, bool) {\n\tswitch o := node.(type) {\n\tcase token.Token:\n\t\tif tok, ok := maybeToken(s); ok {\n\t\t\treturn match(m, tok, node)\n\t\t}\n\t\treturn nil, false\n\tcase string:\n\t\treturn o, string(s) == o\n\tcase types.TypeAndValue:\n\t\treturn o, o.Value != nil && o.Value.String() == string(s)\n\tdefault:\n\t\treturn nil, false\n\t}\n}\n\nfunc (tok Token) Match(m *Matcher, node any) (any, bool) {\n\to, ok := node.(token.Token)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\treturn o, token.Token(tok) == o\n}\n\nfunc (Nil) Match(m *Matcher, node any) (any, bool) {\n\tif isNil(node) {\n\t\treturn nil, true\n\t}\n\tv := reflect.ValueOf(node)\n\tswitch v.Kind() {\n\tcase reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:\n\t\treturn nil, v.IsNil()\n\tdefault:\n\t\treturn nil, false\n\t}\n}\n\nfunc (builtin Builtin) Match(m *Matcher, node any) (any, bool) {\n\tr, ok := match(m, Ident(builtin), node)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tident := r.(*ast.Ident)\n\tobj := m.TypesInfo.ObjectOf(ident)\n\tif obj != types.Universe.Lookup(ident.Name) {\n\t\treturn nil, false\n\t}\n\treturn ident, true\n}\n\nfunc (obj Object) Match(m *Matcher, node any) (any, bool) {\n\tr, ok := match(m, Ident(obj), node)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tident := r.(*ast.Ident)\n\n\tid := m.TypesInfo.ObjectOf(ident)\n\t_, ok = match(m, obj.Name, ident.Name)\n\treturn id, ok\n}\n\nfunc (fn Symbol) Match(m *Matcher, node any) (any, bool) {\n\tvar name string\n\tvar obj types.Object\n\n\tbase := []Node{\n\t\tIdent{Any{}},\n\t\tSelectorExpr{Any{}, Any{}},\n\t}\n\tp := Or{\n\t\tNodes: append(base,\n\t\t\tIndexExpr{Or{Nodes: base}, Any{}},\n\t\t\tIndexListExpr{Or{Nodes: base}, Any{}})}\n\n\tr, ok := match(m, p, node)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\n\tfun := r.(ast.Expr)\n\tswitch idx := fun.(type) {\n\tcase *ast.IndexExpr:\n\t\tfun = idx.X\n\tcase *ast.IndexListExpr:\n\t\tfun = idx.X\n\t}\n\n\tswitch fun := ast.Unparen(fun).(type) {\n\tcase *ast.Ident:\n\t\tobj = m.TypesInfo.ObjectOf(fun)\n\tcase *ast.SelectorExpr:\n\t\tobj = m.TypesInfo.ObjectOf(fun.Sel)\n\tdefault:\n\t\tpanic(\"unreachable\")\n\t}\n\tswitch obj := obj.(type) {\n\tcase *types.Func:\n\t\t// OPT(dh): optimize this similar to code.FuncName\n\t\tname = obj.FullName()\n\tcase *types.Builtin:\n\t\tname = obj.Name()\n\tcase *types.TypeName:\n\t\torigObj := obj\n\t\tfor {\n\t\t\tif obj.Parent() != obj.Pkg().Scope() {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t\tname = types.TypeString(obj.Type(), nil)\n\t\t\t_, ok = match(m, fn.Name, name)\n\t\t\tif ok || !obj.IsAlias() {\n\t\t\t\treturn origObj, ok\n\t\t\t} else {\n\t\t\t\t// FIXME(dh): we should peel away one layer of alias at a time; this is blocked on\n\t\t\t\t// github.com/golang/go/issues/66559\n\t\t\t\tswitch typ := types.Unalias(obj.Type()).(type) {\n\t\t\t\tcase interface{ Obj() *types.TypeName }:\n\t\t\t\t\tobj = typ.Obj()\n\t\t\t\tcase *types.Basic:\n\t\t\t\t\treturn match(m, fn.Name, typ.Name())\n\t\t\t\tdefault:\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tcase *types.Const, *types.Var:\n\t\tif obj.Pkg() == nil {\n\t\t\treturn nil, false\n\t\t}\n\t\tif obj.Parent() != obj.Pkg().Scope() {\n\t\t\treturn nil, false\n\t\t}\n\t\tname = fmt.Sprintf(\"%s.%s\", obj.Pkg().Path(), obj.Name())\n\tdefault:\n\t\treturn nil, false\n\t}\n\n\t_, ok = match(m, fn.Name, name)\n\treturn obj, ok\n}\n\nfunc (or Or) Match(m *Matcher, node any) (any, bool) {\n\tfor _, opt := range or.Nodes {\n\t\tm.push()\n\t\tif ret, ok := match(m, opt, node); ok {\n\t\t\tm.merge()\n\t\t\treturn ret, true\n\t\t} else {\n\t\t\tm.pop()\n\t\t}\n\t}\n\treturn nil, false\n}\n\nfunc (not Not) Match(m *Matcher, node any) (any, bool) {\n\t_, ok := match(m, not.Node, node)\n\tif ok {\n\t\treturn nil, false\n\t}\n\treturn node, true\n}\n\nvar integerLiteralQ = MustParse(`(Or (BasicLit \"INT\" _) (UnaryExpr (Or \"+\" \"-\") (IntegerLiteral _)))`)\n\nfunc (lit IntegerLiteral) Match(m *Matcher, node any) (any, bool) {\n\tmatched, ok := match(m, integerLiteralQ.Root, node)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\ttv, ok := m.TypesInfo.Types[matched.(ast.Expr)]\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tif tv.Value == nil {\n\t\treturn nil, false\n\t}\n\t_, ok = match(m, lit.Value, tv)\n\treturn matched, ok\n}\n\nfunc (texpr TrulyConstantExpression) Match(m *Matcher, node any) (any, bool) {\n\texpr, ok := node.(ast.Expr)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\ttv, ok := m.TypesInfo.Types[expr]\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tif tv.Value == nil {\n\t\treturn nil, false\n\t}\n\ttruly := true\n\tast.Inspect(expr, func(node ast.Node) bool {\n\t\tif _, ok := node.(*ast.Ident); ok {\n\t\t\ttruly = false\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t})\n\tif !truly {\n\t\treturn nil, false\n\t}\n\t_, ok = match(m, texpr.Value, tv)\n\treturn expr, ok\n}\n\nvar (\n\t// Types of fields in go/ast structs that we want to skip\n\trtTokPos = reflect.TypeFor[token.Pos]()\n\t//lint:ignore SA1019 It's deprecated, but we still want to skip the field.\n\trtObject       = reflect.TypeFor[*ast.Object]()\n\trtCommentGroup = reflect.TypeFor[*ast.CommentGroup]()\n)\n\nvar (\n\t_ matcher = Binding{}\n\t_ matcher = Any{}\n\t_ matcher = List{}\n\t_ matcher = String(\"\")\n\t_ matcher = Token(0)\n\t_ matcher = Nil{}\n\t_ matcher = Builtin{}\n\t_ matcher = Object{}\n\t_ matcher = Symbol{}\n\t_ matcher = Or{}\n\t_ matcher = Not{}\n\t_ matcher = IntegerLiteral{}\n\t_ matcher = TrulyConstantExpression{}\n)\n"
  },
  {
    "path": "pattern/parser.go",
    "content": "package pattern\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"iter\"\n\t\"reflect\"\n\t\"strings\"\n)\n\ntype Pattern struct {\n\tRoot Node\n\t// EntryNodes contains instances of ast.Node that could potentially\n\t// initiate a successful match of the pattern.\n\tEntryNodes []ast.Node\n\n\t// SymbolsPattern is a pattern consisting or Any, Or, And, and IndexSymbol,\n\t// that can be used to implement fast rejection of whole packages using\n\t// typeindex.\n\tSymbolsPattern Node\n\n\t// If non-empty, all possible candidate nodes for this pattern can be found\n\t// by finding all call expressions for this list of symbols.\n\tRootCallSymbols []IndexSymbol\n\n\t// Mapping from binding index to binding name\n\tBindings []string\n}\n\nfunc MustParse(s string) Pattern {\n\tp := &Parser{AllowTypeInfo: true}\n\tpat, err := p.Parse(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn pat\n}\n\nfunc symbolToIndexSymbol(name string) IndexSymbol {\n\tif len(name) == 0 {\n\t\treturn IndexSymbol{}\n\t}\n\tif name[0] == '(' {\n\t\tend := strings.IndexAny(name, \")\")\n\t\t// Ensure there's a ), and also that there are at least two more\n\t\t// characters after it, for a dot and an identifier.\n\t\tif end == -1 || end > len(name)-2 {\n\t\t\treturn IndexSymbol{}\n\t\t}\n\t\tpathAndType := strings.TrimPrefix(name[1:end], \"*\")\n\t\tdot := strings.LastIndex(pathAndType, \".\")\n\t\tif dot == -1 {\n\t\t\treturn IndexSymbol{}\n\t\t}\n\t\tpath := pathAndType[:dot]\n\t\ttyp := pathAndType[dot+1:]\n\t\tident := name[end+2:]\n\t\treturn IndexSymbol{path, typ, ident}\n\t} else {\n\t\tdot := strings.LastIndex(name, \".\")\n\t\tif dot == -1 {\n\t\t\treturn IndexSymbol{\"\", \"\", name}\n\t\t}\n\t\tpath := name[:dot]\n\t\tident := name[dot+1:]\n\t\treturn IndexSymbol{path, \"\", ident}\n\t}\n}\n\nfunc collectSymbols(node Node, inSymbol bool) Node {\n\tand := func(c Node, out *And) {\n\t\tswitch cc := c.(type) {\n\t\tcase And:\n\t\t\tout.Nodes = append(out.Nodes, cc.Nodes...)\n\t\tcase Any:\n\t\tcase nil:\n\t\tdefault:\n\t\t\tout.Nodes = append(out.Nodes, c)\n\t\t}\n\t}\n\n\tswitch node := node.(type) {\n\tcase Or:\n\t\ts := Or{}\n\t\tfor _, el := range node.Nodes {\n\t\t\tc := collectSymbols(el, inSymbol)\n\t\t\tswitch cc := c.(type) {\n\t\t\tcase Or:\n\t\t\t\ts.Nodes = append(s.Nodes, cc.Nodes...)\n\t\t\tcase Any:\n\t\t\t\treturn Any{}\n\t\t\tcase nil:\n\t\t\tdefault:\n\t\t\t\ts.Nodes = append(s.Nodes, c)\n\t\t\t}\n\t\t}\n\t\tswitch len(s.Nodes) {\n\t\tcase 0:\n\t\t\treturn nil\n\t\tcase 1:\n\t\t\treturn s.Nodes[0]\n\t\tdefault:\n\t\t\treturn s\n\t\t}\n\tcase Not, Token, nil:\n\t\treturn Any{}\n\tcase Symbol:\n\t\treturn collectSymbols(node.Name, true)\n\tcase String:\n\t\tif !inSymbol {\n\t\t\treturn Any{}\n\t\t}\n\t\t// In logically correct patterns, all Strings that are children of\n\t\t// Symbols describe the names of symbols.\n\t\treturn symbolToIndexSymbol(string(node))\n\tcase Binding:\n\t\treturn collectSymbols(node.Node, inSymbol)\n\tcase Any:\n\t\treturn Any{}\n\tcase List:\n\t\tvar out And\n\t\tand(collectSymbols(node.Head, inSymbol), &out)\n\t\tand(collectSymbols(node.Tail, inSymbol), &out)\n\t\tswitch len(out.Nodes) {\n\t\tcase 0:\n\t\t\treturn Any{}\n\t\tcase 1:\n\t\t\treturn out.Nodes[0]\n\t\tdefault:\n\t\t\treturn out\n\t\t}\n\tdefault:\n\t\tvar out And\n\t\trv := reflect.ValueOf(node)\n\t\tfor i := range rv.NumField() {\n\t\t\tc := collectSymbols(rv.Field(i).Interface().(Node), inSymbol)\n\t\t\tand(c, &out)\n\t\t}\n\t\tswitch len(out.Nodes) {\n\t\tcase 0:\n\t\t\treturn Any{}\n\t\tcase 1:\n\t\t\treturn out.Nodes[0]\n\t\tdefault:\n\t\t\treturn out\n\t\t}\n\t}\n}\n\nfunc collectRootCallSymbols(node Node) []IndexSymbol {\n\troot, ok := node.(CallExpr)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tvar names []String\n\tvar handleSymName func(name Node) bool\n\thandleSymName = func(name Node) bool {\n\t\tswitch name := name.(type) {\n\t\tcase String:\n\t\t\tnames = append(names, name)\n\t\tcase Or:\n\t\t\tfor _, node := range name.Nodes {\n\t\t\t\tif name, ok := node.(String); ok {\n\t\t\t\t\tnames = append(names, name)\n\t\t\t\t} else {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\tcase Binding:\n\t\t\treturn handleSymName(name.Node)\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t}\n\tvar handleRootFun func(node Node) bool\n\thandleRootFun = func(node Node) bool {\n\t\tswitch fun := node.(type) {\n\t\tcase Binding:\n\t\t\treturn handleRootFun(fun.Node)\n\t\tcase Symbol:\n\t\t\treturn handleSymName(fun.Name)\n\t\tcase Or:\n\t\t\tfor _, node := range fun.Nodes {\n\t\t\t\tif sym, ok := node.(Symbol); !ok || !handleSymName(sym.Name) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\t}\n\tif !handleRootFun(root.Fun) {\n\t\treturn nil\n\t}\n\n\tout := make([]IndexSymbol, len(names))\n\tfor i, name := range names {\n\t\tout[i] = symbolToIndexSymbol(string(name))\n\t}\n\treturn out\n}\n\nfunc collectEntryNodes(node Node, m map[reflect.Type]struct{}) {\n\tswitch node := node.(type) {\n\tcase Or:\n\t\tfor _, el := range node.Nodes {\n\t\t\tcollectEntryNodes(el, m)\n\t\t}\n\tcase Not:\n\t\tcollectEntryNodes(node.Node, m)\n\tcase Binding:\n\t\tcollectEntryNodes(node.Node, m)\n\tcase Nil, nil:\n\t\t// this branch is reached via bindings\n\t\tfor _, T := range allTypes {\n\t\t\tm[T] = struct{}{}\n\t\t}\n\tdefault:\n\t\tTs, ok := nodeToASTTypes[reflect.TypeOf(node)]\n\t\tif !ok {\n\t\t\tpanic(fmt.Sprintf(\"internal error: unhandled type %T\", node))\n\t\t}\n\t\tfor _, T := range Ts {\n\t\t\tm[T] = struct{}{}\n\t\t}\n\t}\n}\n\nvar allTypes = []reflect.Type{\n\treflect.TypeFor[*ast.RangeStmt](),\n\treflect.TypeFor[*ast.AssignStmt](),\n\treflect.TypeFor[*ast.IndexExpr](),\n\treflect.TypeFor[*ast.Ident](),\n\treflect.TypeFor[*ast.ValueSpec](),\n\treflect.TypeFor[*ast.GenDecl](),\n\treflect.TypeFor[*ast.BinaryExpr](),\n\treflect.TypeFor[*ast.ForStmt](),\n\treflect.TypeFor[*ast.ArrayType](),\n\treflect.TypeFor[*ast.DeferStmt](),\n\treflect.TypeFor[*ast.MapType](),\n\treflect.TypeFor[*ast.ReturnStmt](),\n\treflect.TypeFor[*ast.SliceExpr](),\n\treflect.TypeFor[*ast.StarExpr](),\n\treflect.TypeFor[*ast.UnaryExpr](),\n\treflect.TypeFor[*ast.SendStmt](),\n\treflect.TypeFor[*ast.SelectStmt](),\n\treflect.TypeFor[*ast.ImportSpec](),\n\treflect.TypeFor[*ast.IfStmt](),\n\treflect.TypeFor[*ast.GoStmt](),\n\treflect.TypeFor[*ast.Field](),\n\treflect.TypeFor[*ast.SelectorExpr](),\n\treflect.TypeFor[*ast.StructType](),\n\treflect.TypeFor[*ast.KeyValueExpr](),\n\treflect.TypeFor[*ast.FuncType](),\n\treflect.TypeFor[*ast.FuncLit](),\n\treflect.TypeFor[*ast.FuncDecl](),\n\treflect.TypeFor[*ast.ChanType](),\n\treflect.TypeFor[*ast.CallExpr](),\n\treflect.TypeFor[*ast.CaseClause](),\n\treflect.TypeFor[*ast.CommClause](),\n\treflect.TypeFor[*ast.CompositeLit](),\n\treflect.TypeFor[*ast.EmptyStmt](),\n\treflect.TypeFor[*ast.SwitchStmt](),\n\treflect.TypeFor[*ast.TypeSwitchStmt](),\n\treflect.TypeFor[*ast.TypeAssertExpr](),\n\treflect.TypeFor[*ast.TypeSpec](),\n\treflect.TypeFor[*ast.InterfaceType](),\n\treflect.TypeFor[*ast.BranchStmt](),\n\treflect.TypeFor[*ast.IncDecStmt](),\n\treflect.TypeFor[*ast.BasicLit](),\n}\n\nvar nodeToASTTypes = map[reflect.Type][]reflect.Type{\n\treflect.TypeFor[String]():                  nil,\n\treflect.TypeFor[Token]():                   nil,\n\treflect.TypeFor[List]():                    {reflect.TypeFor[*ast.BlockStmt](), reflect.TypeFor[*ast.FieldList]()},\n\treflect.TypeFor[Builtin]():                 {reflect.TypeFor[*ast.Ident]()},\n\treflect.TypeFor[Object]():                  {reflect.TypeFor[*ast.Ident]()},\n\treflect.TypeFor[Symbol]():                  {reflect.TypeFor[*ast.Ident](), reflect.TypeFor[*ast.SelectorExpr]()},\n\treflect.TypeFor[Any]():                     allTypes,\n\treflect.TypeFor[RangeStmt]():               {reflect.TypeFor[*ast.RangeStmt]()},\n\treflect.TypeFor[AssignStmt]():              {reflect.TypeFor[*ast.AssignStmt]()},\n\treflect.TypeFor[IndexExpr]():               {reflect.TypeFor[*ast.IndexExpr]()},\n\treflect.TypeFor[Ident]():                   {reflect.TypeFor[*ast.Ident]()},\n\treflect.TypeFor[ValueSpec]():               {reflect.TypeFor[*ast.ValueSpec]()},\n\treflect.TypeFor[GenDecl]():                 {reflect.TypeFor[*ast.GenDecl]()},\n\treflect.TypeFor[BinaryExpr]():              {reflect.TypeFor[*ast.BinaryExpr]()},\n\treflect.TypeFor[ForStmt]():                 {reflect.TypeFor[*ast.ForStmt]()},\n\treflect.TypeFor[ArrayType]():               {reflect.TypeFor[*ast.ArrayType]()},\n\treflect.TypeFor[DeferStmt]():               {reflect.TypeFor[*ast.DeferStmt]()},\n\treflect.TypeFor[MapType]():                 {reflect.TypeFor[*ast.MapType]()},\n\treflect.TypeFor[ReturnStmt]():              {reflect.TypeFor[*ast.ReturnStmt]()},\n\treflect.TypeFor[SliceExpr]():               {reflect.TypeFor[*ast.SliceExpr]()},\n\treflect.TypeFor[StarExpr]():                {reflect.TypeFor[*ast.StarExpr]()},\n\treflect.TypeFor[UnaryExpr]():               {reflect.TypeFor[*ast.UnaryExpr]()},\n\treflect.TypeFor[SendStmt]():                {reflect.TypeFor[*ast.SendStmt]()},\n\treflect.TypeFor[SelectStmt]():              {reflect.TypeFor[*ast.SelectStmt]()},\n\treflect.TypeFor[ImportSpec]():              {reflect.TypeFor[*ast.ImportSpec]()},\n\treflect.TypeFor[IfStmt]():                  {reflect.TypeFor[*ast.IfStmt]()},\n\treflect.TypeFor[GoStmt]():                  {reflect.TypeFor[*ast.GoStmt]()},\n\treflect.TypeFor[Field]():                   {reflect.TypeFor[*ast.Field]()},\n\treflect.TypeFor[SelectorExpr]():            {reflect.TypeFor[*ast.SelectorExpr]()},\n\treflect.TypeFor[StructType]():              {reflect.TypeFor[*ast.StructType]()},\n\treflect.TypeFor[KeyValueExpr]():            {reflect.TypeFor[*ast.KeyValueExpr]()},\n\treflect.TypeFor[FuncType]():                {reflect.TypeFor[*ast.FuncType]()},\n\treflect.TypeFor[FuncLit]():                 {reflect.TypeFor[*ast.FuncLit]()},\n\treflect.TypeFor[FuncDecl]():                {reflect.TypeFor[*ast.FuncDecl]()},\n\treflect.TypeFor[ChanType]():                {reflect.TypeFor[*ast.ChanType]()},\n\treflect.TypeFor[CallExpr]():                {reflect.TypeFor[*ast.CallExpr]()},\n\treflect.TypeFor[CaseClause]():              {reflect.TypeFor[*ast.CaseClause]()},\n\treflect.TypeFor[CommClause]():              {reflect.TypeFor[*ast.CommClause]()},\n\treflect.TypeFor[CompositeLit]():            {reflect.TypeFor[*ast.CompositeLit]()},\n\treflect.TypeFor[EmptyStmt]():               {reflect.TypeFor[*ast.EmptyStmt]()},\n\treflect.TypeFor[SwitchStmt]():              {reflect.TypeFor[*ast.SwitchStmt]()},\n\treflect.TypeFor[TypeSwitchStmt]():          {reflect.TypeFor[*ast.TypeSwitchStmt]()},\n\treflect.TypeFor[TypeAssertExpr]():          {reflect.TypeFor[*ast.TypeAssertExpr]()},\n\treflect.TypeFor[TypeSpec]():                {reflect.TypeFor[*ast.TypeSpec]()},\n\treflect.TypeFor[InterfaceType]():           {reflect.TypeFor[*ast.InterfaceType]()},\n\treflect.TypeFor[BranchStmt]():              {reflect.TypeFor[*ast.BranchStmt]()},\n\treflect.TypeFor[IncDecStmt]():              {reflect.TypeFor[*ast.IncDecStmt]()},\n\treflect.TypeFor[BasicLit]():                {reflect.TypeFor[*ast.BasicLit]()},\n\treflect.TypeFor[IntegerLiteral]():          {reflect.TypeFor[*ast.BasicLit](), reflect.TypeFor[*ast.UnaryExpr]()},\n\treflect.TypeFor[TrulyConstantExpression](): allTypes, // this is an over-approximation, which is fine\n}\n\nvar requiresTypeInfo = map[string]bool{\n\t\"Symbol\":                  true,\n\t\"Builtin\":                 true,\n\t\"Object\":                  true,\n\t\"IntegerLiteral\":          true,\n\t\"TrulyConstantExpression\": true,\n}\n\ntype Parser struct {\n\t// Allow nodes that rely on type information\n\tAllowTypeInfo bool\n\n\tf        *token.File\n\tcur      item\n\tlast     *item\n\tnextItem func() (item, bool)\n\n\tbindings map[string]int\n}\n\nfunc (p *Parser) bindingIndex(name string) int {\n\tif p.bindings == nil {\n\t\tp.bindings = map[string]int{}\n\t}\n\tif idx, ok := p.bindings[name]; ok {\n\t\treturn idx\n\t}\n\tidx := len(p.bindings)\n\tp.bindings[name] = idx\n\treturn idx\n}\n\nfunc (p *Parser) Parse(s string) (Pattern, error) {\n\tf := token.NewFileSet().AddFile(\"<input>\", -1, len(s))\n\n\t// Run the lexer iterator as a coroutine.\n\t// The parser will call 'next' to consume each item.\n\t// After the parser returns, we must call 'stop' to\n\t// terminate the coroutine.\n\tnext, stop := iter.Pull(lex(f, s))\n\tdefer stop()\n\n\tp.cur = item{}\n\tp.last = nil\n\tp.f = f\n\tp.nextItem = next\n\n\t// Parse.\n\troot, err := p.node()\n\tif err != nil {\n\t\treturn Pattern{}, err\n\t}\n\t// Consume final EOF token.\n\tif item, ok := next(); !ok || item.typ != itemEOF {\n\t\treturn Pattern{}, fmt.Errorf(\"unexpected token %s after end of pattern\", item.typ)\n\t}\n\n\tif len(p.bindings) > 64 {\n\t\treturn Pattern{}, errors.New(\"encountered more than 64 bindings\")\n\t}\n\n\tbindings := make([]string, len(p.bindings))\n\tfor name, idx := range p.bindings {\n\t\tbindings[idx] = name\n\t}\n\n\t_, isSymbol := root.(Symbol)\n\tsym := collectSymbols(root, isSymbol)\n\trootSyms := collectRootCallSymbols(root)\n\trelevantMap := map[reflect.Type]struct{}{}\n\tcollectEntryNodes(root, relevantMap)\n\trelevantNodes := make([]ast.Node, 0, len(relevantMap))\n\tfor k := range relevantMap {\n\t\trelevantNodes = append(relevantNodes, reflect.Zero(k).Interface().(ast.Node))\n\t}\n\treturn Pattern{\n\t\tRoot:            root,\n\t\tEntryNodes:      relevantNodes,\n\t\tSymbolsPattern:  sym,\n\t\tRootCallSymbols: rootSyms,\n\t\tBindings:        bindings,\n\t}, nil\n}\n\nfunc (p *Parser) next() item {\n\tif p.last != nil {\n\t\tn := *p.last\n\t\tp.last = nil\n\t\treturn n\n\t}\n\tvar ok bool\n\tp.cur, ok = p.nextItem()\n\tif !ok {\n\t\tp.cur = item{typ: eof}\n\t}\n\treturn p.cur\n}\n\nfunc (p *Parser) rewind() {\n\tp.last = &p.cur\n}\n\nfunc (p *Parser) peek() item {\n\tn := p.next()\n\tp.rewind()\n\treturn n\n}\n\nfunc (p *Parser) accept(typ itemType) (item, bool) {\n\tn := p.next()\n\tif n.typ == typ {\n\t\treturn n, true\n\t}\n\tp.rewind()\n\treturn item{}, false\n}\n\nfunc (p *Parser) unexpectedToken(valid string) error {\n\tif p.cur.typ == itemError {\n\t\treturn fmt.Errorf(\"error lexing input: %s\", p.cur.val)\n\t}\n\tvar got string\n\tswitch p.cur.typ {\n\tcase itemTypeName, itemVariable, itemString:\n\t\tgot = p.cur.val\n\tdefault:\n\t\tgot = \"'\" + p.cur.typ.String() + \"'\"\n\t}\n\n\tpos := p.f.Position(token.Pos(p.cur.pos))\n\treturn fmt.Errorf(\"%s: expected %s, found %s\", pos, valid, got)\n}\n\nfunc (p *Parser) node() (Node, error) {\n\tif _, ok := p.accept(itemLeftParen); !ok {\n\t\treturn nil, p.unexpectedToken(\"'('\")\n\t}\n\ttyp, ok := p.accept(itemTypeName)\n\tif !ok {\n\t\treturn nil, p.unexpectedToken(\"Node type\")\n\t}\n\n\tvar objs []Node\n\tfor {\n\t\tif _, ok := p.accept(itemRightParen); ok {\n\t\t\tbreak\n\t\t} else {\n\t\t\tp.rewind()\n\t\t\tobj, err := p.object()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tobjs = append(objs, obj)\n\t\t}\n\t}\n\n\tnode, err := p.populateNode(typ.val, objs)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif node, ok := node.(Binding); ok {\n\t\tnode.idx = p.bindingIndex(node.Name)\n\t}\n\treturn node, nil\n}\n\nfunc populateNode(typ string, objs []Node, allowTypeInfo bool) (Node, error) {\n\tT, ok := structNodes[typ]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unknown node %s\", typ)\n\t}\n\n\tif !allowTypeInfo && requiresTypeInfo[typ] {\n\t\treturn nil, fmt.Errorf(\"Node %s requires type information\", typ)\n\t}\n\n\tpv := reflect.New(T)\n\tv := pv.Elem()\n\n\tif v.NumField() == 1 {\n\t\tf := v.Field(0)\n\t\tif f.Type().Kind() == reflect.Slice {\n\t\t\t// Variadic node\n\t\t\tf.Set(reflect.AppendSlice(f, reflect.ValueOf(objs)))\n\t\t\treturn v.Interface().(Node), nil\n\t\t}\n\t}\n\n\tn := -1\n\tfor i := 0; i < T.NumField(); i++ {\n\t\tif !T.Field(i).IsExported() {\n\t\t\tbreak\n\t\t}\n\t\tn = i\n\t}\n\n\tif len(objs) != n+1 {\n\t\treturn nil, fmt.Errorf(\"tried to initialize node %s with %d values, expected %d\", typ, len(objs), n+1)\n\t}\n\n\tfor i := 0; i < v.NumField(); i++ {\n\t\tif !T.Field(i).IsExported() {\n\t\t\tbreak\n\t\t}\n\t\tf := v.Field(i)\n\t\tif f.Kind() == reflect.String {\n\t\t\tif obj, ok := objs[i].(String); ok {\n\t\t\t\tf.Set(reflect.ValueOf(string(obj)))\n\t\t\t} else {\n\t\t\t\treturn nil, fmt.Errorf(\"first argument of (Binding name node) must be string, but got %s\", objs[i])\n\t\t\t}\n\t\t} else {\n\t\t\tf.Set(reflect.ValueOf(objs[i]))\n\t\t}\n\t}\n\treturn v.Interface().(Node), nil\n}\n\nfunc (p *Parser) populateNode(typ string, objs []Node) (Node, error) {\n\treturn populateNode(typ, objs, p.AllowTypeInfo)\n}\n\nvar structNodes = map[string]reflect.Type{\n\t\"Any\":                     reflect.TypeFor[Any](),\n\t\"Ellipsis\":                reflect.TypeFor[Ellipsis](),\n\t\"List\":                    reflect.TypeFor[List](),\n\t\"Binding\":                 reflect.TypeFor[Binding](),\n\t\"RangeStmt\":               reflect.TypeFor[RangeStmt](),\n\t\"AssignStmt\":              reflect.TypeFor[AssignStmt](),\n\t\"IndexExpr\":               reflect.TypeFor[IndexExpr](),\n\t\"Ident\":                   reflect.TypeFor[Ident](),\n\t\"Builtin\":                 reflect.TypeFor[Builtin](),\n\t\"ValueSpec\":               reflect.TypeFor[ValueSpec](),\n\t\"GenDecl\":                 reflect.TypeFor[GenDecl](),\n\t\"BinaryExpr\":              reflect.TypeFor[BinaryExpr](),\n\t\"ForStmt\":                 reflect.TypeFor[ForStmt](),\n\t\"ArrayType\":               reflect.TypeFor[ArrayType](),\n\t\"DeferStmt\":               reflect.TypeFor[DeferStmt](),\n\t\"MapType\":                 reflect.TypeFor[MapType](),\n\t\"ReturnStmt\":              reflect.TypeFor[ReturnStmt](),\n\t\"SliceExpr\":               reflect.TypeFor[SliceExpr](),\n\t\"StarExpr\":                reflect.TypeFor[StarExpr](),\n\t\"UnaryExpr\":               reflect.TypeFor[UnaryExpr](),\n\t\"SendStmt\":                reflect.TypeFor[SendStmt](),\n\t\"SelectStmt\":              reflect.TypeFor[SelectStmt](),\n\t\"ImportSpec\":              reflect.TypeFor[ImportSpec](),\n\t\"IfStmt\":                  reflect.TypeFor[IfStmt](),\n\t\"GoStmt\":                  reflect.TypeFor[GoStmt](),\n\t\"Field\":                   reflect.TypeFor[Field](),\n\t\"SelectorExpr\":            reflect.TypeFor[SelectorExpr](),\n\t\"StructType\":              reflect.TypeFor[StructType](),\n\t\"KeyValueExpr\":            reflect.TypeFor[KeyValueExpr](),\n\t\"FuncType\":                reflect.TypeFor[FuncType](),\n\t\"FuncLit\":                 reflect.TypeFor[FuncLit](),\n\t\"FuncDecl\":                reflect.TypeFor[FuncDecl](),\n\t\"ChanType\":                reflect.TypeFor[ChanType](),\n\t\"CallExpr\":                reflect.TypeFor[CallExpr](),\n\t\"CaseClause\":              reflect.TypeFor[CaseClause](),\n\t\"CommClause\":              reflect.TypeFor[CommClause](),\n\t\"CompositeLit\":            reflect.TypeFor[CompositeLit](),\n\t\"EmptyStmt\":               reflect.TypeFor[EmptyStmt](),\n\t\"SwitchStmt\":              reflect.TypeFor[SwitchStmt](),\n\t\"TypeSwitchStmt\":          reflect.TypeFor[TypeSwitchStmt](),\n\t\"TypeAssertExpr\":          reflect.TypeFor[TypeAssertExpr](),\n\t\"TypeSpec\":                reflect.TypeFor[TypeSpec](),\n\t\"InterfaceType\":           reflect.TypeFor[InterfaceType](),\n\t\"BranchStmt\":              reflect.TypeFor[BranchStmt](),\n\t\"IncDecStmt\":              reflect.TypeFor[IncDecStmt](),\n\t\"BasicLit\":                reflect.TypeFor[BasicLit](),\n\t\"Object\":                  reflect.TypeFor[Object](),\n\t\"Symbol\":                  reflect.TypeFor[Symbol](),\n\t\"Or\":                      reflect.TypeFor[Or](),\n\t\"Not\":                     reflect.TypeFor[Not](),\n\t\"IntegerLiteral\":          reflect.TypeFor[IntegerLiteral](),\n\t\"TrulyConstantExpression\": reflect.TypeFor[TrulyConstantExpression](),\n}\n\nfunc (p *Parser) object() (Node, error) {\n\tn := p.next()\n\tswitch n.typ {\n\tcase itemLeftParen:\n\t\tp.rewind()\n\t\tnode, err := p.node()\n\t\tif err != nil {\n\t\t\treturn node, err\n\t\t}\n\t\tif p.peek().typ == itemColon {\n\t\t\tp.next()\n\t\t\ttail, err := p.object()\n\t\t\tif err != nil {\n\t\t\t\treturn node, err\n\t\t\t}\n\t\t\treturn List{Head: node, Tail: tail}, nil\n\t\t}\n\t\treturn node, nil\n\tcase itemLeftBracket:\n\t\tp.rewind()\n\t\treturn p.array()\n\tcase itemVariable:\n\t\tv := n\n\t\tif v.val == \"nil\" {\n\t\t\treturn Nil{}, nil\n\t\t}\n\t\tvar b Binding\n\t\tif _, ok := p.accept(itemAt); ok {\n\t\t\to, err := p.node()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tb = Binding{\n\t\t\t\tName: v.val,\n\t\t\t\tNode: o,\n\t\t\t\tidx:  p.bindingIndex(v.val),\n\t\t\t}\n\t\t} else {\n\t\t\tp.rewind()\n\t\t\tb = Binding{\n\t\t\t\tName: v.val,\n\t\t\t\tidx:  p.bindingIndex(v.val),\n\t\t\t}\n\t\t}\n\t\tif p.peek().typ == itemColon {\n\t\t\tp.next()\n\t\t\ttail, err := p.object()\n\t\t\tif err != nil {\n\t\t\t\treturn b, err\n\t\t\t}\n\t\t\treturn List{Head: b, Tail: tail}, nil\n\t\t}\n\t\treturn b, nil\n\tcase itemBlank:\n\t\tif p.peek().typ == itemColon {\n\t\t\tp.next()\n\t\t\ttail, err := p.object()\n\t\t\tif err != nil {\n\t\t\t\treturn Any{}, err\n\t\t\t}\n\t\t\treturn List{Head: Any{}, Tail: tail}, nil\n\t\t}\n\t\treturn Any{}, nil\n\tcase itemString:\n\t\treturn String(n.val), nil\n\tdefault:\n\t\treturn nil, p.unexpectedToken(\"object\")\n\t}\n}\n\nfunc (p *Parser) array() (Node, error) {\n\tif _, ok := p.accept(itemLeftBracket); !ok {\n\t\treturn nil, p.unexpectedToken(\"'['\")\n\t}\n\n\tvar objs []Node\n\tfor {\n\t\tif _, ok := p.accept(itemRightBracket); ok {\n\t\t\tbreak\n\t\t} else {\n\t\t\tp.rewind()\n\t\t\tobj, err := p.object()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tobjs = append(objs, obj)\n\t\t}\n\t}\n\n\ttail := List{}\n\tfor i := len(objs) - 1; i >= 0; i-- {\n\t\tl := List{\n\t\t\tHead: objs[i],\n\t\t\tTail: tail,\n\t\t}\n\t\ttail = l\n\t}\n\treturn tail, nil\n}\n\n/*\nNode ::= itemLeftParen itemTypeName Object* itemRightParen\nObject ::= Node | Array | Binding | itemVariable | itemBlank | itemString\nArray := itemLeftBracket Object* itemRightBracket\nArray := Object itemColon Object\nBinding ::= itemVariable itemAt Node\n*/\n"
  },
  {
    "path": "pattern/parser_test.go",
    "content": "package pattern\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\tgoparser \"go/parser\"\n\t\"go/token\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"honnef.co/go/tools/debug\"\n)\n\nfunc TestParse(t *testing.T) {\n\tinputs := []string{\n\t\t`(Binding \"name\" _)`,\n\t\t`(Binding \"name\" _:[])`,\n\t\t`(Binding \"name\" _:_:[])`,\n\t}\n\n\tp := Parser{}\n\tfor _, input := range inputs {\n\t\tif _, err := p.Parse(input); err != nil {\n\t\t\tt.Errorf(\"failed to parse %q: %s\", input, err)\n\t\t}\n\t}\n}\n\nfunc FuzzParse(f *testing.F) {\n\tvar files []*ast.File\n\tfset := token.NewFileSet()\n\n\t// Ideally we'd check against as much source code as possible, but that's fairly slow, on the order of 500ms per\n\t// pattern when checking against the whole standard library.\n\t//\n\t// We pick the runtime package in the hopes that it contains the most diverse, and weird, code.\n\t//\n\t//lint:ignore SA1019 runtime.GOROOT is deprecated, we'll have to update this eventually\n\tfilepath.Walk(runtime.GOROOT()+\"/src/runtime\", func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\t// XXX error handling\n\t\t\tpanic(err)\n\t\t}\n\t\tif !strings.HasSuffix(path, \".go\") {\n\t\t\treturn nil\n\t\t}\n\t\tf, err := goparser.ParseFile(fset, path, nil, goparser.SkipObjectResolution)\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\t\tfiles = append(files, f)\n\t\treturn nil\n\t})\n\n\tparse := func(in string, allowTypeInfo bool) (Pattern, bool) {\n\t\tp := Parser{\n\t\t\tAllowTypeInfo: allowTypeInfo,\n\t\t}\n\t\tpat, err := p.Parse(string(in))\n\t\tif err != nil {\n\t\t\tif strings.Contains(err.Error(), \"internal error\") {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\treturn Pattern{}, false\n\t\t}\n\t\treturn pat, true\n\t}\n\n\tf.Fuzz(func(t *testing.T, in []byte) {\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\tstr := fmt.Sprint(err)\n\t\t\t\tif strings.Contains(str, \"binding already created:\") {\n\t\t\t\t\t// This is an invalid pattern, not a real failure\n\t\t\t\t} else {\n\t\t\t\t\t// Re-panic the original panic\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t\t// Parse twice, once with AllowTypeInfo set to true to exercise the parser, and once with it set to false so we\n\t\t// can actually use it in Match, as we don't have type information available.\n\n\t\tpat, ok := parse(string(in), true)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\t// Make sure we can turn it back into a string\n\t\t_ = pat.Root.String()\n\n\t\tpat, ok = parse(string(in), false)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\t// Make sure we can turn it back into a string\n\t\t_ = pat.Root.String()\n\n\t\tentryNodesMap := make(map[reflect.Type]struct{})\n\t\tfor _, node := range pat.EntryNodes {\n\t\t\tentryNodesMap[reflect.TypeOf(node)] = struct{}{}\n\t\t}\n\n\t\t// Don't check patterns with too many relevant nodes; it's too expensive\n\t\tif len(pat.EntryNodes) < 20 {\n\t\t\t// Make sure trying to match nodes doesn't panic\n\t\t\tfor _, f := range files {\n\t\t\t\tast.Inspect(f, func(node ast.Node) bool {\n\t\t\t\t\trt := reflect.TypeOf(node)\n\t\t\t\t\t// We'd prefer calling Match on all nodes, not just those the pattern deems relevant, to find more bugs.\n\t\t\t\t\t// However, doing so has a 10x cost in execution time.\n\t\t\t\t\tif _, ok := entryNodesMap[rt]; ok {\n\t\t\t\t\t\tMatch(pat, node)\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestMatchAlias(t *testing.T) {\n\tp1 := MustParse(`(CallExpr (Symbol \"foo.Alias\") _)`)\n\tp2 := MustParse(`(CallExpr (Symbol \"int\") _)`)\n\n\tf, _, info, err := debug.TypeCheck(`\npackage pkg\ntype Alias = int\nfunc _() { _ = Alias(0) }\n`)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tm := &Matcher{\n\t\tTypesInfo: info,\n\t}\n\tnode := f.Decls[1].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt).Rhs[0]\n\n\tif debug.AliasesEnabled() {\n\t\t// Check that we can match on the name of the alias\n\t\tif ok := m.Match(p1, node); !ok {\n\t\t\tt.Errorf(\"%s did not match\", p1.Root)\n\t\t}\n\t}\n\n\t// Check that we can match on the name of the alias's target\n\tif ok := m.Match(p2, node); !ok {\n\t\tt.Errorf(\"%s did not match\", p2.Root)\n\t}\n}\n\nfunc TestCollectSymbols(t *testing.T) {\n\tfor _, tt := range []struct {\n\t\tin  string\n\t\tout string\n\t}{\n\t\t{\n\t\t\t`(Or (Symbol \"foo\") (Symbol \"bar\"))`,\n\t\t\t`(Or (IndexSymbol \"\" \"\" \"foo\") (IndexSymbol \"\" \"\" \"bar\"))`,\n\t\t},\n\t\t{\n\t\t\t`(CallExpr (Symbol \"foo\") [(Symbol \"bar\") (Symbol \"baz\")])`,\n\t\t\t`(And (IndexSymbol \"\" \"\" \"foo\") (IndexSymbol \"\" \"\" \"bar\") (IndexSymbol \"\" \"\" \"baz\"))`,\n\t\t},\n\t\t{\n\t\t\t`(Symbol (Or \"foo\" \"bar\"))`,\n\t\t\t`(Or (IndexSymbol \"\" \"\" \"foo\") (IndexSymbol \"\" \"\" \"bar\"))`,\n\t\t},\n\t\t{\n\t\t\t// (Or) never matches anything, so we do need the \"foo\" symbol for a\n\t\t\t// successful match\n\t\t\t`(Or (Symbol \"foo\") (Or))`,\n\t\t\t`(IndexSymbol \"\" \"\" \"foo\")`,\n\t\t},\n\t\t{\n\t\t\t// This tests (And ...)\n\t\t\t`(BasicLit (Symbol \"foo\") (Ident \"bar\"))`,\n\t\t\t`(IndexSymbol \"\" \"\" \"foo\")`,\n\t\t},\n\t\t{\n\t\t\t`(Or (Symbol \"foo\") (Ident _))`,\n\t\t\t`_`,\n\t\t},\n\t\t{\n\t\t\t`(Or (Symbol \"foo\") (EmptyStmt))`,\n\t\t\t`_`,\n\t\t},\n\t\t{\n\t\t\t`(Or (Symbol \"foo\") nil)`,\n\t\t\t`_`,\n\t\t},\n\t\t{\n\t\t\t`(Symbol \"example.com/foo.Get\")`,\n\t\t\t`(IndexSymbol \"example.com/foo\" \"\" \"Get\")`,\n\t\t},\n\t\t{\n\t\t\t`(Symbol \"(*example.com/foo.Client).Get\")`,\n\t\t\t`(IndexSymbol \"example.com/foo\" \"Client\" \"Get\")`,\n\t\t},\n\n\t\t// Don't crash on malformed symbols\n\t\t{\n\t\t\t`(Symbol \"\")`,\n\t\t\t`(IndexSymbol \"\" \"\" \"\")`,\n\t\t},\n\t\t{\n\t\t\t`(Symbol \"foo.\")`,\n\t\t\t`(IndexSymbol \"foo\" \"\" \"\")`,\n\t\t},\n\t\t{\n\t\t\t`(Symbol \"(foo\")`,\n\t\t\t`(IndexSymbol \"\" \"\" \"\")`,\n\t\t},\n\t\t{\n\t\t\t`(Symbol \"(foo)\")`,\n\t\t\t`(IndexSymbol \"\" \"\" \"\")`,\n\t\t},\n\t\t{\n\t\t\t`(Symbol \"(foo.Bar)\")`,\n\t\t\t`(IndexSymbol \"\" \"\" \"\")`,\n\t\t},\n\t\t{\n\t\t\t`(Symbol \"(foo.Bar).\")`,\n\t\t\t`(IndexSymbol \"foo\" \"Bar\" \"\")`,\n\t\t},\n\t\t{\n\t\t\t`(Symbol \"(foo.Bar.\")`,\n\t\t\t`(IndexSymbol \"\" \"\" \"\")`,\n\t\t},\n\t\t{\n\t\t\t`(Symbol \"(foo).Bar\")`,\n\t\t\t`(IndexSymbol \"\" \"\" \"\")`,\n\t\t},\n\t} {\n\t\tp := &Parser{AllowTypeInfo: true}\n\t\tpat, err := p.Parse(tt.in)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\ts := pat.SymbolsPattern.String()\n\t\tif s != tt.out {\n\t\t\tt.Fatalf(\"Symbol requirements for %s: got %s, want %s\", tt.in, s, tt.out)\n\t\t}\n\t}\n}\n\nfunc BenchmarkParser(b *testing.B) {\n\tconst input = `\n\t(CallExpr\n\t\t(Symbol\n\t\t\tname@(Or\n\t\t\t\t\"math/rand.Int31n\"\n\t\t\t\t\"math/rand.Int63n\"\n\t\t\t\t\"math/rand.Intn\"\n\t\t\t\t\"(*math/rand.Rand).Int31n\"\n\t\t\t\t\"(*math/rand.Rand).Int63n\"\n\t\t\t\t\"(*math/rand.Rand).Intn\"\n\n\t\t\t\t\"math/rand/v2.Int32N\"\n\t\t\t\t\"math/rand/v2.Int64N\"\n\t\t\t\t\"math/rand/v2.IntN\"\n\t\t\t\t\"math/rand/v2.N\"\n\t\t\t\t\"math/rand/v2.Uint32N\"\n\t\t\t\t\"math/rand/v2.Uint64N\"\n\t\t\t\t\"math/rand/v2.UintN\"\n\n\t\t\t\t\"(*math/rand/v2.Rand).Int32N\"\n\t\t\t\t\"(*math/rand/v2.Rand).Int64N\"\n\t\t\t\t\"(*math/rand/v2.Rand).IntN\"\n\t\t\t\t\"(*math/rand/v2.Rand).Uint32N\"\n\t\t\t\t\"(*math/rand/v2.Rand).Uint64N\"\n\t\t\t\t\"(*math/rand/v2.Rand).UintN\"))\n\t\t[(IntegerLiteral \"1\")])`\n\n\tfor range b.N {\n\t\tp := &Parser{AllowTypeInfo: true}\n\t\t_, err := p.Parse(input)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pattern/pattern.go",
    "content": "package pattern\n\nimport (\n\t\"fmt\"\n\t\"go/token\"\n\t\"reflect\"\n\t\"strings\"\n)\n\nvar (\n\t_ Node = Ellipsis{}\n\t_ Node = Binding{}\n\t_ Node = RangeStmt{}\n\t_ Node = AssignStmt{}\n\t_ Node = IndexExpr{}\n\t_ Node = IndexListExpr{}\n\t_ Node = Ident{}\n\t_ Node = Builtin{}\n\t_ Node = String(\"\")\n\t_ Node = Any{}\n\t_ Node = ValueSpec{}\n\t_ Node = List{}\n\t_ Node = GenDecl{}\n\t_ Node = BinaryExpr{}\n\t_ Node = ForStmt{}\n\t_ Node = ArrayType{}\n\t_ Node = DeferStmt{}\n\t_ Node = MapType{}\n\t_ Node = ReturnStmt{}\n\t_ Node = SliceExpr{}\n\t_ Node = StarExpr{}\n\t_ Node = UnaryExpr{}\n\t_ Node = SendStmt{}\n\t_ Node = SelectStmt{}\n\t_ Node = ImportSpec{}\n\t_ Node = IfStmt{}\n\t_ Node = GoStmt{}\n\t_ Node = Field{}\n\t_ Node = SelectorExpr{}\n\t_ Node = StructType{}\n\t_ Node = KeyValueExpr{}\n\t_ Node = FuncType{}\n\t_ Node = FuncLit{}\n\t_ Node = FuncDecl{}\n\t_ Node = Token(0)\n\t_ Node = ChanType{}\n\t_ Node = CallExpr{}\n\t_ Node = CaseClause{}\n\t_ Node = CommClause{}\n\t_ Node = CompositeLit{}\n\t_ Node = EmptyStmt{}\n\t_ Node = SwitchStmt{}\n\t_ Node = TypeSwitchStmt{}\n\t_ Node = TypeAssertExpr{}\n\t_ Node = TypeSpec{}\n\t_ Node = InterfaceType{}\n\t_ Node = BranchStmt{}\n\t_ Node = IncDecStmt{}\n\t_ Node = BasicLit{}\n\t_ Node = Nil{}\n\t_ Node = Object{}\n\t_ Node = Symbol{}\n\t_ Node = Not{}\n\t_ Node = Or{}\n\t_ Node = IntegerLiteral{}\n\t_ Node = TrulyConstantExpression{}\n)\n\ntype Symbol struct {\n\tName Node\n}\n\ntype Token token.Token\n\ntype Nil struct {\n}\n\ntype Ellipsis struct {\n\tElt Node\n}\n\ntype IncDecStmt struct {\n\tX   Node\n\tTok Node\n}\n\ntype BranchStmt struct {\n\tTok   Node\n\tLabel Node\n}\n\ntype InterfaceType struct {\n\tMethods Node\n}\n\ntype TypeSpec struct {\n\tName Node\n\tType Node\n}\n\ntype TypeAssertExpr struct {\n\tX    Node\n\tType Node\n}\n\ntype TypeSwitchStmt struct {\n\tInit   Node\n\tAssign Node\n\tBody   Node\n}\n\ntype SwitchStmt struct {\n\tInit Node\n\tTag  Node\n\tBody Node\n}\n\ntype EmptyStmt struct {\n}\n\ntype CompositeLit struct {\n\tType Node\n\tElts Node\n}\n\ntype CommClause struct {\n\tComm Node\n\tBody Node\n}\n\ntype CaseClause struct {\n\tList Node\n\tBody Node\n}\n\ntype CallExpr struct {\n\tFun  Node\n\tArgs Node\n\t// XXX handle ellipsis\n}\n\n// TODO(dh): add a ChanDir node, and a way of instantiating it.\n\ntype ChanType struct {\n\tDir   Node\n\tValue Node\n}\n\ntype FuncDecl struct {\n\tRecv Node\n\tName Node\n\tType Node\n\tBody Node\n}\n\ntype FuncLit struct {\n\tType Node\n\tBody Node\n}\n\ntype FuncType struct {\n\tParams  Node\n\tResults Node\n}\n\ntype KeyValueExpr struct {\n\tKey   Node\n\tValue Node\n}\n\ntype StructType struct {\n\tFields Node\n}\n\ntype SelectorExpr struct {\n\tX   Node\n\tSel Node\n}\n\ntype Field struct {\n\tNames Node\n\tType  Node\n\tTag   Node\n}\n\ntype GoStmt struct {\n\tCall Node\n}\n\ntype IfStmt struct {\n\tInit Node\n\tCond Node\n\tBody Node\n\tElse Node\n}\n\ntype ImportSpec struct {\n\tName Node\n\tPath Node\n}\n\ntype SelectStmt struct {\n\tBody Node\n}\n\ntype ArrayType struct {\n\tLen Node\n\tElt Node\n}\n\ntype DeferStmt struct {\n\tCall Node\n}\n\ntype MapType struct {\n\tKey   Node\n\tValue Node\n}\n\ntype ReturnStmt struct {\n\tResults Node\n}\n\ntype SliceExpr struct {\n\tX    Node\n\tLow  Node\n\tHigh Node\n\tMax  Node\n}\n\ntype StarExpr struct {\n\tX Node\n}\n\ntype UnaryExpr struct {\n\tOp Node\n\tX  Node\n}\n\ntype SendStmt struct {\n\tChan  Node\n\tValue Node\n}\n\ntype Binding struct {\n\tName string\n\tNode Node\n\n\tidx int\n}\n\ntype RangeStmt struct {\n\tKey   Node\n\tValue Node\n\tTok   Node\n\tX     Node\n\tBody  Node\n}\n\ntype AssignStmt struct {\n\tLhs Node\n\tTok Node\n\tRhs Node\n}\n\ntype IndexExpr struct {\n\tX     Node\n\tIndex Node\n}\n\ntype IndexListExpr struct {\n\tX       Node\n\tIndices Node\n}\n\ntype Node interface {\n\tString() string\n\tisNode()\n}\n\ntype Ident struct {\n\tName Node\n}\n\ntype Object struct {\n\tName Node\n}\n\ntype Builtin struct {\n\tName Node\n}\n\ntype String string\n\ntype Any struct{}\n\ntype ValueSpec struct {\n\tNames  Node\n\tType   Node\n\tValues Node\n}\n\ntype List struct {\n\tHead Node\n\tTail Node\n}\n\ntype GenDecl struct {\n\tTok   Node\n\tSpecs Node\n}\n\ntype BasicLit struct {\n\tKind  Node\n\tValue Node\n}\n\n// An IntegerLiteral is a constant expression made up of only integer basic literals and the \"+\" and \"-\" unary operators.\n// That is, 0, -4, -+42 are all integer literals, but 1 + 2 is not.\ntype IntegerLiteral struct {\n\tValue Node\n}\n\ntype BinaryExpr struct {\n\tX  Node\n\tOp Node\n\tY  Node\n}\n\ntype ForStmt struct {\n\tInit Node\n\tCond Node\n\tPost Node\n\tBody Node\n}\n\ntype Or struct {\n\tNodes []Node\n}\n\ntype And struct {\n\tNodes []Node\n}\n\ntype Not struct {\n\tNode Node\n}\n\n// A TrulyConstantExpression is a constant expression that does not make use of any identifiers.\n// It is constant even under varying build tags.\ntype TrulyConstantExpression struct {\n\tValue Node\n}\n\ntype IndexSymbol struct {\n\tPath  string\n\tType  string\n\tIdent string\n}\n\nfunc stringify(n Node) string {\n\tv := reflect.ValueOf(n)\n\tvar parts []string\n\tparts = append(parts, v.Type().Name())\n\tfor i := 0; i < v.NumField(); i++ {\n\t\tparts = append(parts, fmt.Sprintf(\"%s\", v.Field(i)))\n\t}\n\treturn \"(\" + strings.Join(parts, \" \") + \")\"\n}\n\nfunc (stmt AssignStmt) String() string              { return stringify(stmt) }\nfunc (expr IndexExpr) String() string               { return stringify(expr) }\nfunc (expr IndexListExpr) String() string           { return stringify(expr) }\nfunc (id Ident) String() string                     { return stringify(id) }\nfunc (spec ValueSpec) String() string               { return stringify(spec) }\nfunc (decl GenDecl) String() string                 { return stringify(decl) }\nfunc (lit BasicLit) String() string                 { return stringify(lit) }\nfunc (expr BinaryExpr) String() string              { return stringify(expr) }\nfunc (stmt ForStmt) String() string                 { return stringify(stmt) }\nfunc (stmt RangeStmt) String() string               { return stringify(stmt) }\nfunc (typ ArrayType) String() string                { return stringify(typ) }\nfunc (stmt DeferStmt) String() string               { return stringify(stmt) }\nfunc (typ MapType) String() string                  { return stringify(typ) }\nfunc (stmt ReturnStmt) String() string              { return stringify(stmt) }\nfunc (expr SliceExpr) String() string               { return stringify(expr) }\nfunc (expr StarExpr) String() string                { return stringify(expr) }\nfunc (expr UnaryExpr) String() string               { return stringify(expr) }\nfunc (stmt SendStmt) String() string                { return stringify(stmt) }\nfunc (spec ImportSpec) String() string              { return stringify(spec) }\nfunc (stmt SelectStmt) String() string              { return stringify(stmt) }\nfunc (stmt IfStmt) String() string                  { return stringify(stmt) }\nfunc (stmt IncDecStmt) String() string              { return stringify(stmt) }\nfunc (stmt GoStmt) String() string                  { return stringify(stmt) }\nfunc (field Field) String() string                  { return stringify(field) }\nfunc (expr SelectorExpr) String() string            { return stringify(expr) }\nfunc (typ StructType) String() string               { return stringify(typ) }\nfunc (expr KeyValueExpr) String() string            { return stringify(expr) }\nfunc (typ FuncType) String() string                 { return stringify(typ) }\nfunc (lit FuncLit) String() string                  { return stringify(lit) }\nfunc (decl FuncDecl) String() string                { return stringify(decl) }\nfunc (stmt BranchStmt) String() string              { return stringify(stmt) }\nfunc (expr CallExpr) String() string                { return stringify(expr) }\nfunc (clause CaseClause) String() string            { return stringify(clause) }\nfunc (typ ChanType) String() string                 { return stringify(typ) }\nfunc (clause CommClause) String() string            { return stringify(clause) }\nfunc (lit CompositeLit) String() string             { return stringify(lit) }\nfunc (stmt EmptyStmt) String() string               { return stringify(stmt) }\nfunc (typ InterfaceType) String() string            { return stringify(typ) }\nfunc (stmt SwitchStmt) String() string              { return stringify(stmt) }\nfunc (expr TypeAssertExpr) String() string          { return stringify(expr) }\nfunc (spec TypeSpec) String() string                { return stringify(spec) }\nfunc (stmt TypeSwitchStmt) String() string          { return stringify(stmt) }\nfunc (nil Nil) String() string                      { return \"nil\" }\nfunc (builtin Builtin) String() string              { return stringify(builtin) }\nfunc (obj Object) String() string                   { return stringify(obj) }\nfunc (fn Symbol) String() string                    { return stringify(fn) }\nfunc (el Ellipsis) String() string                  { return stringify(el) }\nfunc (not Not) String() string                      { return stringify(not) }\nfunc (lit IntegerLiteral) String() string           { return stringify(lit) }\nfunc (expr TrulyConstantExpression) String() string { return stringify(expr) }\nfunc (sym IndexSymbol) String() string {\n\treturn fmt.Sprintf(\"(IndexSymbol %q %q %q)\", sym.Path, sym.Type, sym.Ident)\n}\n\nfunc (or Or) String() string {\n\tvar s strings.Builder\n\ts.WriteString(\"(Or\")\n\tfor _, node := range or.Nodes {\n\t\ts.WriteString(\" \")\n\t\ts.WriteString(node.String())\n\t}\n\ts.WriteString(\")\")\n\treturn s.String()\n}\n\nfunc (and And) String() string {\n\tvar s strings.Builder\n\ts.WriteString(\"(And\")\n\tfor _, node := range and.Nodes {\n\t\ts.WriteString(\" \")\n\t\ts.WriteString(node.String())\n\t}\n\ts.WriteString(\")\")\n\treturn s.String()\n}\n\nfunc isProperList(l List) bool {\n\tif l.Head == nil && l.Tail == nil {\n\t\treturn true\n\t}\n\tswitch tail := l.Tail.(type) {\n\tcase nil:\n\t\treturn false\n\tcase List:\n\t\treturn isProperList(tail)\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (l List) String() string {\n\tif l.Head == nil && l.Tail == nil {\n\t\treturn \"[]\"\n\t}\n\n\tif isProperList(l) {\n\t\t// pretty-print the list\n\t\tvar objs []string\n\t\tfor l.Head != nil {\n\t\t\tobjs = append(objs, l.Head.String())\n\t\t\tl = l.Tail.(List)\n\t\t}\n\t\treturn fmt.Sprintf(\"[%s]\", strings.Join(objs, \" \"))\n\t}\n\n\treturn fmt.Sprintf(\"%s:%s\", l.Head, l.Tail)\n}\n\nfunc (bind Binding) String() string {\n\tif bind.Node == nil {\n\t\treturn bind.Name\n\t}\n\treturn fmt.Sprintf(\"%s@%s\", bind.Name, bind.Node)\n}\n\nfunc (s String) String() string { return fmt.Sprintf(\"%q\", string(s)) }\n\nfunc (tok Token) String() string {\n\treturn fmt.Sprintf(\"%q\", strings.ToUpper(token.Token(tok).String()))\n}\n\nfunc (Any) String() string { return \"_\" }\n\nfunc (AssignStmt) isNode()              {}\nfunc (IndexExpr) isNode()               {}\nfunc (IndexListExpr) isNode()           {}\nfunc (Ident) isNode()                   {}\nfunc (ValueSpec) isNode()               {}\nfunc (GenDecl) isNode()                 {}\nfunc (BasicLit) isNode()                {}\nfunc (BinaryExpr) isNode()              {}\nfunc (ForStmt) isNode()                 {}\nfunc (RangeStmt) isNode()               {}\nfunc (ArrayType) isNode()               {}\nfunc (DeferStmt) isNode()               {}\nfunc (MapType) isNode()                 {}\nfunc (ReturnStmt) isNode()              {}\nfunc (SliceExpr) isNode()               {}\nfunc (StarExpr) isNode()                {}\nfunc (UnaryExpr) isNode()               {}\nfunc (SendStmt) isNode()                {}\nfunc (ImportSpec) isNode()              {}\nfunc (SelectStmt) isNode()              {}\nfunc (IfStmt) isNode()                  {}\nfunc (IncDecStmt) isNode()              {}\nfunc (GoStmt) isNode()                  {}\nfunc (Field) isNode()                   {}\nfunc (SelectorExpr) isNode()            {}\nfunc (StructType) isNode()              {}\nfunc (KeyValueExpr) isNode()            {}\nfunc (FuncType) isNode()                {}\nfunc (FuncLit) isNode()                 {}\nfunc (FuncDecl) isNode()                {}\nfunc (BranchStmt) isNode()              {}\nfunc (CallExpr) isNode()                {}\nfunc (CaseClause) isNode()              {}\nfunc (ChanType) isNode()                {}\nfunc (CommClause) isNode()              {}\nfunc (CompositeLit) isNode()            {}\nfunc (EmptyStmt) isNode()               {}\nfunc (InterfaceType) isNode()           {}\nfunc (SwitchStmt) isNode()              {}\nfunc (TypeAssertExpr) isNode()          {}\nfunc (TypeSpec) isNode()                {}\nfunc (TypeSwitchStmt) isNode()          {}\nfunc (Nil) isNode()                     {}\nfunc (Builtin) isNode()                 {}\nfunc (Object) isNode()                  {}\nfunc (Symbol) isNode()                  {}\nfunc (Ellipsis) isNode()                {}\nfunc (Or) isNode()                      {}\nfunc (And) isNode()                     {}\nfunc (List) isNode()                    {}\nfunc (String) isNode()                  {}\nfunc (Token) isNode()                   {}\nfunc (Any) isNode()                     {}\nfunc (Binding) isNode()                 {}\nfunc (Not) isNode()                     {}\nfunc (IntegerLiteral) isNode()          {}\nfunc (TrulyConstantExpression) isNode() {}\nfunc (IndexSymbol) isNode()             {}\n"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/0001cdcefc5f03f99c21d4ef8232d8f0d8510d9c48e8105c927bc70ac02034a9",
    "content": "go test fuzz v1\n[]byte(\"(AssignStmt ident \\\":=\\\" expr)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/00ec3673b415e2f6fc4a3f0d31413096921fbd1faa1cbabdd3637480af027a72",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (SelectorExpr recv (Ident \\\"String\\\")) [])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/02f183192c9bcfbb22db5afa08e5a9a84babfca022726d0121f42c68d3feecee",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"(CallExpr (Symbol \\\\\\\"math.Pow\\\\\\\") [x (IntegerLiteral n)])\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/04fca5bfcc4a67c0d97de75fd6dc13a4a3e5c2dc68e5061f7bcb7e19852efe56",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"\\\\n\\\\t\\\\t(Or\\\\n\\\\t\\\\t\\\\t(CallExpr\\\\n\\\\t\\\\t\\\\t\\\\tfn@(Or\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Print\\\\\\\")\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Sprint\\\\\\\")\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Println\\\\\\\")\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Sprintln\\\\\\\"))\\\\n\\\\t\\\\t\\\\t\\\\t[(CallExpr (Symbol \\\\\\\"fmt.Sprintf\\\\\\\") f:_)])\\\\n\\\\t\\\\t\\\\t(CallExpr\\\\n\\\\t\\\\t\\\\t\\\\tfn@(Or\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Fprint\\\\\\\")\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Fprintln\\\\\\\"))\\\\n\\\\t\\\\t\\\\t\\\\t[_ (CallExpr (Symbol \\\\\\\"fmt.Sprintf\\\\\\\") f:_)]))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/05eea82b6791ec62e197e6128c608c67f5393ff98e94a9c1ba1311e763778749",
    "content": "go test fuzz v1\n[]byte(\"(Or________________________________________________________________________________________________________________________________)\")\n"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/06b3cbf8b7806ca08ce1ca466e83488ca32abb5db6b0ca4b07c54aa7be47adf3",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (SelectorExpr lhs (Ident \\\"Equal\\\")) rhs)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/09c3a6a518c0e44fe60591523655ba4d7dcf62cb477f7e316a51e089adea74c2",
    "content": "go test fuzz v1\n[]byte(\"(AssignStmt x tok@(Or \\\"+=\\\" \\\"-=\\\") (BasicLit \\\"INT\\\" \\\"1\\\"))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/0a21c29e926184ebb3c293c9cea3465ef5e1fc5c1b81be7d0770d5d69ee838a3",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t\\t(ForStmt\\n\\t\\t\\tnil nil nil\\n\\t\\t\\tselect@(SelectStmt\\n\\t\\t\\t\\t(CommClause\\n\\t\\t\\t\\t\\t(Or\\n\\t\\t\\t\\t\\t\\t(UnaryExpr \\\"<-\\\" _)\\n\\t\\t\\t\\t\\t\\t(AssignStmt _ _ (UnaryExpr \\\"<-\\\" _)))\\n\\t\\t\\t\\t\\t_)))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/0ce7ffb3713ec9373531b2903b8f8751e280cdae2b625dcf35dc1fcd88c592bf",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"\\\\n\\\\t(CallExpr\\\\n\\\\t\\\\tfn@(Or\\\\n\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Sprint\\\\\\\")\\\\n\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Sprintf\\\\\\\"))\\\\n\\\\t\\\\t[lit@(BasicLit \\\\\\\"STRING\\\\\\\" _)])\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/170704499ec0c05bf39fb37f6c5604e13624c4fb531e41305b2439308e370f35",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr fun@(SelectorExpr _ (Ident \\\"Seek\\\")) [arg1@(SelectorExpr (Ident \\\"io\\\") (Ident (Or \\\"SeekStart\\\" \\\"SeekCurrent\\\" \\\"SeekEnd\\\"))) arg2])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/1a3c741fba42577fac3c5035a3d44e5a78bcefa11f9ccc3bb2919376d984e4a2",
    "content": "go test fuzz v1\n[]byte(\"(BinaryExpr left@(TrulyConstantExpression _) tok@(Or \\\"==\\\" \\\"!=\\\") right@(Not (TrulyConstantExpression _)))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/1eb6c2e8b8e0be47a019f0345b68ebfdba5f05804204e810166d1fe7c12e8556",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t(IfStmt\\n\\t\\tnil\\n\\t\\t(BinaryExpr x@(Object _) \\\"!=\\\" (Builtin \\\"nil\\\"))\\n\\t\\t[(RangeStmt _ _ _ x _)]\\n\\t\\tnil)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/27e5f99d63fed488c4e9c3ac4a1e364f809ad894cb109aacc9bd6a85c015fdb7",
    "content": "go test fuzz v1\n[]byte(\"(IfStmt nil cond [(ReturnStmt [ret@(Builtin (Or \\\"true\\\" \\\"false\\\"))])] nil)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/2bac99d4a450641e3ae239588965c64323b1ee9eb2351cc53019d430d3a59efa",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"\\\\n\\\\t(CallExpr\\\\n\\\\t\\\\t(SelectorExpr recv (Ident \\\\\\\"Write\\\\\\\"))\\\\n\\\\t\\\\t(CallExpr (ArrayType nil (Ident \\\\\\\"byte\\\\\\\"))\\\\n\\\\t\\\\t\\\\t(CallExpr\\\\n\\\\t\\\\t\\\\t\\\\tfn@(Or\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Sprint\\\\\\\")\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Sprintf\\\\\\\")\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Sprintln\\\\\\\"))\\\\n\\\\t\\\\t\\\\t\\\\targs)\\\\n\\\\t))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/2c72a4a6b571446d5374dc5174fa44767bdcc8197e38c54738e50f8b58903230",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr(Ident(SelectorExpr(Ident\\\"\\\")(Ident\\\"\\\")))[])\")\n"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/2f1cdb43e9c62bdb5f8777bc2cb4eee3e8fe173c4361f54833c48d06833ec8fe",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t\\t(IfStmt\\n\\t\\t\\tnil\\n\\t\\t\\t(BinaryExpr lhs@(Object _) \\\"!=\\\" (Builtin \\\"nil\\\"))\\n\\t\\t\\t[\\n\\t\\t\\t\\tifstmt@(IfStmt\\n\\t\\t\\t\\t\\t(AssignStmt [(Ident \\\"_\\\") ok@(Object _)] _ [(TypeAssertExpr lhs _)])\\n\\t\\t\\t\\t\\tok\\n\\t\\t\\t\\t\\t_\\n\\t\\t\\t\\t\\tnil)\\n\\t\\t\\t]\\n\\t\\t\\tnil)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/312c49b9d41ad52e7beaa65ab01f5416e4f4d1db78b4e0001260ac888256b609",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (Ident \\\"string\\\") [arg])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/3148b044a5e00e508bfd9ac4d139e032503a590c36bd458a8291b77502d13561",
    "content": "go test fuzz v1\n[]byte(\"(AssignStmt x@(Object _) \\\":=\\\" assign@(Builtin b@(Or \\\"true\\\" \\\"false\\\")))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/31ac2ece486bde345a4ac42fb989efa8835e72e82e357d5d82a313d6ba03eca2",
    "content": "go test fuzz v1\n[]byte(\"(ForStmt nil nil nil if@(IfStmt nil cond (BranchStmt \\\"BREAK\\\" nil) nil):_)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/359bf5d248c22a3fc8d67de10279802663a767d4bf2d11dad3209bee13953ee0",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (Builtin \\\"make\\\") [typ size size])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/3895395d667f576d7f3891a63e4cc0157b2ec73dbe55745c1cba65f31e8cc5db",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (SelectorExpr (CallExpr (SelectorExpr recv (Ident \\\"Query\\\")) []) (Ident meth)) _)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/3a3ef35129ccc131fc582363751397ad5723fb8ae891c31eaa5ad86ba402a27e",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"(CallExpr (Symbol \\\\\\\"fmt.Sprintf\\\\\\\") [format arg])\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/3d78313ab191ebe8647428cd6d896208cb6dcfdd19eb87ae388315548176445a",
    "content": "go test fuzz v1\n[]byte(\"(UnaryExpr \\\"!\\\" expr@(BinaryExpr _ _ _))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/3e0e018aca3103af7824d729c88c028b8e0d60d3de223c786f46acac3e910cdb",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t(IfStmt\\n\\t\\t(AssignStmt\\n\\t\\t\\t[(Ident \\\"_\\\") ok@(Ident _)]\\n\\t\\t\\t\\\":=\\\"\\n\\t\\t\\t(IndexExpr m key))\\n\\t\\tok\\n\\t\\t[call@(CallExpr (Builtin \\\"delete\\\") [m key])]\\n\\t\\tnil)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/3f66b015db9a62175f277eab5f76a62397c681b7e4ed6564f452e6159d4cb454",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t\\t(AssignStmt\\n\\t\\t\\t(Ident \\\"_\\\") _ recv@(UnaryExpr \\\"<-\\\" _))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/4115b01752356dd12fd6499da369daec6031f62d315aecc4afad56c97f61b904",
    "content": "go test fuzz v1\n[]byte(\"(BinaryExpr duration \\\"*\\\" (SelectorExpr (Ident \\\"time\\\") (Ident \\\"Second\\\")))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/465963a68302ca54f21c75fc3f680d6a5e1065682fb05a1350ed105883436a82",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (SelectorExpr (Ident \\\"bytes\\\") (Ident \\\"Equal\\\")) args)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/47857edd56b46ac9c16e788e9295d1dafb910c345899aafd618ddaa12793f4f9",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"(CallExpr (Symbol \\\\\\\"errors.New\\\\\\\") [(CallExpr (Symbol \\\\\\\"fmt.Sprintf\\\\\\\") args)])\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/4dec90e6083b5e195501df63d8e1ed6813b623bef60ad8d9e0a1df1f251a58f3",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"\\\\n\\\\t(CallExpr\\\\n\\\\t\\\\t(Symbol\\\\n\\\\t\\\\t\\\\tname@(Or\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"math/rand.Int31n\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"math/rand.Int63n\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"math/rand.Intn\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"(*math/rand.Rand).Int31n\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"(*math/rand.Rand).Int63n\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"(*math/rand.Rand).Intn\\\\\\\"))\\\\n\\\\t\\\\t[(IntegerLiteral \\\\\\\"1\\\\\\\")])\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/51390f40de42348adb99c613cd8367db404851ce3ea1a4e02ea316b5b7e915b7",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t\\t(ForStmt\\n\\t\\t\\t(AssignStmt initvar@(Ident _) _ (IntegerLiteral \\\"0\\\"))\\n\\t\\t\\t(BinaryExpr initvar \\\"<\\\" limit@(Ident _))\\n\\t\\t\\t(IncDecStmt initvar \\\"++\\\")\\n\\t\\t\\t[(AssignStmt\\n\\t\\t\\t\\t(IndexExpr slice@(Ident _) initvar)\\n\\t\\t\\t\\t\\\"=\\\"\\n\\t\\t\\t\\t(IndexExpr slice (BinaryExpr offset@(Ident _) \\\"+\\\" initvar)))])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/53da8fdd88cd66de33bbdbbf564e2b14b69d02f32102d8a96171ed4b05dbc92e",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"\\\\n\\\\t\\\\t(CallExpr\\\\n\\\\t\\\\t\\\\t(Symbol\\\\n\\\\t\\\\t\\\\t\\\\t(Or\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Fatal\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Fatalln\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Panic\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Panicln\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Print\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Println\\\\\\\"))\\\\n\\\\t\\\\t\\\\t[(CallExpr (Symbol \\\\\\\"fmt.Sprintf\\\\\\\") args)])\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/56a234ae7b32577f770d5b997f037de709344d7be6fd9ca6e1f44fc8c4367f5b",
    "content": "go test fuzz v1\n[]byte(\"(ForStmt a a a(IfStmt a b(BranchStmt nil a)a):a)\")\n"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/5baab7b6c2c18988c27aefc55f5d48c6ac583f790fb6763cda34375f9b07da40",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t(Or\\n\\t\\t(IfStmt\\n\\t\\t\\t(AssignStmt [(Ident \\\"_\\\") ok@(Ident _)] \\\":=\\\" indexexpr@(IndexExpr _ _))\\n\\t\\t\\tok\\n\\t\\t\\tset@(AssignStmt indexexpr \\\"=\\\" (CallExpr (Builtin \\\"append\\\") indexexpr:values))\\n\\t\\t\\t(AssignStmt indexexpr \\\"=\\\" (CompositeLit _ values)))\\n\\t\\t(IfStmt\\n\\t\\t\\t(AssignStmt [(Ident \\\"_\\\") ok] \\\":=\\\" indexexpr@(IndexExpr _ _))\\n\\t\\t\\tok\\n\\t\\t\\tset@(AssignStmt indexexpr \\\"+=\\\" value)\\n\\t\\t\\t(AssignStmt indexexpr \\\"=\\\" value))\\n\\t\\t(IfStmt\\n\\t\\t\\t(AssignStmt [(Ident \\\"_\\\") ok] \\\":=\\\" indexexpr@(IndexExpr _ _))\\n\\t\\t\\tok\\n\\t\\t\\tset@(IncDecStmt indexexpr \\\"++\\\")\\n\\t\\t\\t(AssignStmt indexexpr \\\"=\\\" (IntegerLiteral \\\"1\\\"))))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/5cc91809f9225a218b9cfb3a31d5baed3c5a44b5da3a74184fa97abe3bbf178f",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (SelectorExpr (Ident \\\"time\\\") (Ident \\\"Since\\\")) [arg])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/5d9a745f26174c61e5ab0966e4821f75b71de80345be52d4b81aa1515158b735",
    "content": "go test fuzz v1\n[]byte(\"(BinaryExpr\\n\\t\\t(UnaryExpr \\\"&\\\" _)\\n\\t\\t(Or \\\"==\\\" \\\"!=\\\")\\n\\t\\t(Builtin \\\"nil\\\"))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/5e8383a425cf9bc34f43d60f7586184ae7a544e3ad10405ef7aca57246c2ab66",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"\\\\n\\\\t(BinaryExpr\\\\n\\\\t\\\\t(CallExpr fun@(Symbol (Or \\\\\\\"strings.ToLower\\\\\\\" \\\\\\\"strings.ToUpper\\\\\\\")) [a])\\\\n \\\\t\\\\ttok@(Or \\\\\\\"==\\\\\\\" \\\\\\\"!=\\\\\\\")\\\\n \\\\t\\\\t(CallExpr fun [b]))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/60c36a14214281c0c2c31599563afec69016f469a0f25222a9500e307b159d11",
    "content": "go test fuzz v1\n[]byte(\"(Or[a a])\")\n"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/614ea1474d223cb45716d531aa8afac2dfd52938aeb38c64b70a351f0cf509b2",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"(CallExpr (SelectorExpr (CallExpr (Symbol \\\\\\\"time.Now\\\\\\\") []) (Symbol \\\\\\\"(time.Time).Sub\\\\\\\")) [arg])\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/6490b1471fef1f39",
    "content": "go test fuzz v1\n[]byte(\"(Or_:)E)\")\n"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/6a4d6ea339df8f59816483834329cc4310816de0223bd3607b2af6c91367a59b",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t\\t(AssignStmt\\n\\t\\t\\t[_ (Ident \\\"_\\\")]\\n\\t\\t\\t_\\n\\t\\t\\t(Or\\n\\t\\t\\t\\t(IndexExpr _ _)\\n\\t\\t\\t\\t(UnaryExpr \\\"<-\\\" _))) \")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/6aa9975401e9a24c46284ea6ea1740740fc58950a021c56e1376c2e108ee3b90",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (SelectorExpr recv (Ident \\\"Bytes\\\")) [])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/6ac1f5e27fbe6d979efae1abf9b2439a824b83f4b2a27508dbeb5dc95b4f9960",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"\\\\n\\\\t(Or\\\\n\\\\t\\\\t(CallExpr\\\\n\\\\t\\\\t\\\\t(Symbol (Or\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fmt.Print\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fmt.Println\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fmt.Sprint\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fmt.Sprintln\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Fatal\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Fatalln\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Panic\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Panicln\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Print\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"log.Println\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"(*log.Logger).Fatal\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"(*log.Logger).Fatalln\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"(*log.Logger).Panic\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"(*log.Logger).Panicln\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"(*log.Logger).Print\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"(*log.Logger).Println\\\\\\\")) args)\\\\n\\\\n\\\\t\\\\t(CallExpr (Symbol (Or\\\\n\\\\t\\\\t\\\\t\\\\\\\"fmt.Fprint\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\\\\"fmt.Fprintln\\\\\\\")) _:args))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/6fa1e1e283fd220866a9e5878510db574b761fbd5a0e863e66f40fd4acbbaf07",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"(SelectStmt (CommClause (UnaryExpr \\\\\\\"<-\\\\\\\" (CallExpr (Symbol \\\\\\\"time.After\\\\\\\") [arg])) body))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/71e2fa0db72c309e630267beac45c90d37e4b8f9d2d2ed52100d1abca7b72965",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t(Or\\n\\t\\t(UnaryExpr\\n\\t\\t\\t\\\"-\\\"\\n\\t\\t\\t(BasicLit \\\"FLOAT\\\" \\\"0.0\\\"))\\n\\n\\t\\t(UnaryExpr\\n\\t\\t\\t\\\"-\\\"\\n\\t\\t\\t(CallExpr conv@(Object (Or \\\"float32\\\" \\\"float64\\\")) lit@(Or (BasicLit \\\"INT\\\" \\\"0\\\") (BasicLit \\\"FLOAT\\\" \\\"0.0\\\"))))\\n\\n\\t\\t(CallExpr\\n\\t\\t\\tconv@(Object (Or \\\"float32\\\" \\\"float64\\\"))\\n\\t\\t\\t(UnaryExpr \\\"-\\\" lit@(BasicLit \\\"INT\\\" \\\"0\\\"))))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/76d91998f39bf2e25bd361453a73968274ffe16677cf02d872222d4c799552f8",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"(Or\\\\n\\\\t(CallExpr fn@(Symbol \\\\\\\"strings.Replace\\\\\\\") [_ _ _ lit@(IntegerLiteral \\\\\\\"-1\\\\\\\")])\\\\n\\\\t(CallExpr fn@(Symbol \\\\\\\"strings.SplitN\\\\\\\") [_ _ lit@(IntegerLiteral \\\\\\\"-1\\\\\\\")])\\\\n\\\\t(CallExpr fn@(Symbol \\\\\\\"strings.SplitAfterN\\\\\\\") [_ _ lit@(IntegerLiteral \\\\\\\"-1\\\\\\\")])\\\\n\\\\t(CallExpr fn@(Symbol \\\\\\\"bytes.Replace\\\\\\\") [_ _ _ lit@(IntegerLiteral \\\\\\\"-1\\\\\\\")])\\\\n\\\\t(CallExpr fn@(Symbol \\\\\\\"bytes.SplitN\\\\\\\") [_ _ lit@(IntegerLiteral \\\\\\\"-1\\\\\\\")])\\\\n\\\\t(CallExpr fn@(Symbol \\\\\\\"bytes.SplitAfterN\\\\\\\") [_ _ lit@(IntegerLiteral \\\\\\\"-1\\\\\\\")]))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/78eaf491672242d08770ab22b67853f639c767f65346de39c6f3e677b1cd879d",
    "content": "go test fuzz v1\n[]byte(\"(UnaryExpr \\\"&\\\" (StarExpr obj))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/7ba6359207886a1c2c7bbe254835555e87a037ecd3af0301a11a43ec2287c487",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"(CallExpr fun@(Symbol _) (Builtin \\\\\\\"nil\\\\\\\"):_)\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/7ec87621ab148929b69125a04edd13ff104007ca0d8dff12f281753ea93ffb80",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"(BinaryExpr (CallExpr (Symbol \\\\\\\"bytes.Compare\\\\\\\") args) op@(Or \\\\\\\"==\\\\\\\" \\\\\\\"!=\\\\\\\") (IntegerLiteral \\\\\\\"0\\\\\\\"))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/8203d4ee0690ca0d0c4b907e1f1c8d6c1724c4771ec3a685b56b440f52b4282a",
    "content": "go test fuzz v1\n[]byte(\"(BinaryExpr _ \\\"%\\\" (IntegerLiteral \\\"1\\\"))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/8324b925e52410ab88b6265538881346436b67d95ad808b8f9220a84b0772ab7",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t\\t(IfStmt\\n\\t\\t\\t(AssignStmt [(Ident \\\"_\\\") ok@(Object _)] _ [(TypeAssertExpr assert@(Object _) _)])\\n\\t\\t\\t(Or\\n\\t\\t\\t\\t(BinaryExpr ok \\\"&&\\\" (BinaryExpr assert \\\"!=\\\" (Builtin \\\"nil\\\")))\\n\\t\\t\\t\\t(BinaryExpr (BinaryExpr assert \\\"!=\\\" (Builtin \\\"nil\\\")) \\\"&&\\\" ok))\\n\\t\\t\\t_\\n\\t\\t\\t_)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/84e67732ffe4ba2d8fdb8cfc8690804579623dbc9c56a378ca483f088348296a",
    "content": "go test fuzz v1\n[]byte(\"(ReturnStmt [ret@(Builtin (Or \\\"true\\\" \\\"false\\\"))])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/87839b3497143dd5ea14963b78c011edceb40d13fe1d8cd9b894a81b5dae2200",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (Builtin \\\"make\\\") [typ size@(IntegerLiteral \\\"0\\\")])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/87f42498d6f57dc40c9972487f0e35d9820acbbce6cf61f3b90dabaa9cb8a8fc",
    "content": "go test fuzz v1\n[]byte(\"(AssignStmt[a(Ident\\\"_\\\")]a(Or(IndexExpr_a)(UnaryExpr\\\"\\\"a)))\")\n"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/90a846c5b88ccf4fe765113a3580ecc90a5cf083a97f0bc4b3bb53a1f00e3fd8",
    "content": "go test fuzz v1\n[]byte(\"(SliceExpr x@(Object _) low (CallExpr (Builtin \\\"len\\\") [x]) nil)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/9437b751fb0f1f07f5dcb8c8a10d0f3d4470a77b7ec77df6be872a109184bd1b",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"(CallExpr (Symbol \\\\\\\"time.Sleep\\\\\\\") lit@(IntegerLiteral value))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/94b7dc35d595dd794b4f65cd35f94ae8fe7c7214e6da8caa69f0b841e9a099af",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr _ [(CallExpr sel@(SelectorExpr recv _) [])])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/9d603847ed1c030c81f2289ee576971cd63564cc811afb5c18d5a51db7aefa76",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"\\\\n\\\\t\\\\t(CallExpr\\\\n\\\\t\\\\t\\\\tsel@(SelectorExpr\\\\n\\\\t\\\\t\\\\t\\\\trecv\\\\n\\\\t\\\\t\\\\t\\\\t(Ident\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tname@(Or\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Error\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Fatal\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Fatalln\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Log\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Panic\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Panicln\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Print\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Println\\\\\\\"\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Skip\\\\\\\")))\\\\n\\\\t\\\\t\\\\t[(CallExpr (Symbol \\\\\\\"fmt.Sprintf\\\\\\\") args)])\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/a3b8af4d027db37d44e58995ed2ab3cd9f2cb415669287e9e7ce7186534b4b1f",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (SelectorExpr (Ident \\\"time\\\") (Ident \\\"Until\\\")) [arg])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/aa520290f4868dc3c01f15d2769941654a404b87327f5dde790c99fc2c63d875",
    "content": "go test fuzz v1\n[]byte(\"(IfStmt (AssignStmt [obj@(Ident _) ok@(Ident _)] \\\":=\\\" assert@(TypeAssertExpr obj _)) ok _ elseBranch)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/ac1b69c690b399207dd7fe32f03a12d2731fa2d1704f6b15cfdc7f772b0f3187",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"\\\\n\\\\t(CallExpr\\\\n\\\\t\\\\t(SelectorExpr recv (Ident \\\\\\\"WriteString\\\\\\\"))\\\\n\\\\t\\\\t(CallExpr\\\\n\\\\t\\\\t\\\\tfn@(Or\\\\n\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Sprint\\\\\\\")\\\\n\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Sprintf\\\\\\\")\\\\n\\\\t\\\\t\\\\t\\\\t(Symbol \\\\\\\"fmt.Sprintln\\\\\\\"))\\\\n\\\\t\\\\t\\\\targs))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/ad86b3632aca0a27fef3d6d79de5c2bcf1c21f7a6caa1260aab964edc21f3f65",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"\\\\n\\\\t(GoStmt\\\\n\\\\t\\\\t(CallExpr\\\\n\\\\t\\\\t\\\\t(FuncLit\\\\n\\\\t\\\\t\\\\t\\\\t_\\\\n\\\\t\\\\t\\\\t\\\\tcall@(CallExpr (Symbol \\\\\\\"(*sync.WaitGroup).Add\\\\\\\") _):_) _))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/af10598249def731ec19ebffa3cbc464892d0e445dbefab9ccf578eae136236a",
    "content": "go test fuzz v1\n[]byte(\"(IntegerLiteral tv)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/afe5949d38d9171e39ad413d31abfab6bf45d066b700b4e84a232a6b3aa53085",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"\\\\n\\\\t\\\\t(Or\\\\n\\\\t\\\\t\\\\t(RangeStmt\\\\n\\\\t\\\\t\\\\t\\\\tkey@(Ident _) value@(Ident _) \\\\\\\":=\\\\\\\" src\\\\n\\\\t\\\\t\\\\t\\\\t[(AssignStmt (IndexExpr dst key) \\\\\\\"=\\\\\\\" value)])\\\\n\\\\t\\\\t\\\\t(RangeStmt\\\\n\\\\t\\\\t\\\\t\\\\tkey@(Ident _) nil \\\\\\\":=\\\\\\\" src\\\\n\\\\t\\\\t\\\\t\\\\t[(AssignStmt (IndexExpr dst key) \\\\\\\"=\\\\\\\" (IndexExpr src key))])\\\\n\\\\t\\\\t\\\\t(ForStmt\\\\n\\\\t\\\\t\\\\t\\\\t(AssignStmt key@(Ident _) \\\\\\\":=\\\\\\\" (IntegerLiteral \\\\\\\"0\\\\\\\"))\\\\n\\\\t\\\\t\\\\t\\\\t(BinaryExpr key \\\\\\\"<\\\\\\\" (CallExpr (Symbol \\\\\\\"len\\\\\\\") [src]))\\\\n\\\\t\\\\t\\\\t\\\\t(IncDecStmt key \\\\\\\"++\\\\\\\")\\\\n\\\\t\\\\t\\\\t\\\\t[(AssignStmt (IndexExpr dst key) \\\\\\\"=\\\\\\\" (IndexExpr src key))]))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/b44bceab2d84f09950aa80d8541c18e31a3d5dde6e874fd0bfe2e4ce54606db0",
    "content": "go test fuzz v1\n[]byte(\"(AssignStmt a a@(Or\\\"0\\\"\\\"0\\\")(BasicLit\\\"000\\\"\\\"\\\"))\")\n"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/b553c9e015253a9e3d4e202fdb2d90764151e24219f26f7510a433d30323666e",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t(Or\\n\\t\\t(BinaryExpr\\n\\t\\t\\t(IntegerLiteral \\\"0\\\")\\n\\t\\t\\t\\\">\\\"\\n\\t\\t\\t(CallExpr builtin@(Builtin (Or \\\"len\\\" \\\"cap\\\")) _))\\n\\t\\t(BinaryExpr\\n\\t\\t\\t(CallExpr builtin@(Builtin (Or \\\"len\\\" \\\"cap\\\")) _)\\n\\t\\t\\t\\\"<\\\"\\n\\t\\t\\t(IntegerLiteral \\\"0\\\")))\\n\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/b6a22c4a4f5e0cf4a291f2d6f03860631075934e4069959665d1f8097c69d0d0",
    "content": "go test fuzz v1\n[]byte(\"(UnaryExpr \\\"!\\\" (CallExpr (SelectorExpr (Ident \\\"bytes\\\") (Ident \\\"Equal\\\")) args))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/b95053e6ea7644faad4e0f2e5f308ca66d6a05c47bf36d0fde268fc12e09ca63",
    "content": "go test fuzz v1\n[]byte(\"\\n(Or\\n\\t(RangeStmt\\n\\t\\t(Ident \\\"_\\\")\\n\\t\\tval@(Object _)\\n\\t\\t_\\n\\t\\tx\\n\\t\\t[(AssignStmt [lhs] \\\"=\\\" [(CallExpr (Builtin \\\"append\\\") [lhs val])])])\\n\\t(RangeStmt\\n\\t\\tidx@(Ident _)\\n\\t\\tnil\\n\\t\\t_\\n\\t\\tx\\n\\t\\t[(AssignStmt [lhs] \\\"=\\\" [(CallExpr (Builtin \\\"append\\\") [lhs (IndexExpr x idx)])])])\\n\\t(RangeStmt\\n\\t\\tidx@(Ident _)\\n\\t\\tnil\\n\\t\\t_\\n\\t\\tx\\n\\t\\t[(AssignStmt val@(Object _) \\\":=\\\" (IndexExpr x idx))\\n\\t\\t(AssignStmt [lhs] \\\"=\\\" [(CallExpr (Builtin \\\"append\\\") [lhs val])])]))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/ba95d1477ea1b35a949c6b469077d908b1cbcaf7fbf3ce9ef544bfeb24f877fb",
    "content": "go test fuzz v1\n[]byte(\"(BinaryExpr duration \\\"*\\\" (SelectorExpr (Ident \\\"time\\\") (Ident \\\"Nanosecond\\\")))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/bb62ca358e19867f7d31400cb2a65aac1e918308212c43d10cca21feeb9c99d2",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t\\t(TypeSwitchStmt\\n\\t\\t\\tnil\\n\\t\\t\\texpr@(TypeAssertExpr ident@(Ident _) _)\\n\\t\\t\\tbody)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/c1a2c8141751527604100e865db8d0e711ce25fc5c291b7702752496ac4b2546",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr fun [arg2 arg1])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/c30ca6d4801d71144c641960df6919115149d2b6fae5f7d9b2bac2b8cd6b8d25",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"(AssignStmt target@(Ident _) \\\\\\\"=\\\\\\\" (CallExpr typ@(Symbol (Or \\\\\\\"sort.Float64Slice\\\\\\\" \\\\\\\"sort.IntSlice\\\\\\\" \\\\\\\"sort.StringSlice\\\\\\\")) [target]))\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/c5f48734d853b82016955671d916daaf72da20a5f8335dddf7640fab1f5a3acb",
    "content": "go test fuzz v1\n[]byte(\"(BinaryExpr right tok left)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/c6d06d254dee12276b9b46ef9be863a1eefc4d0673946a706ec7a164625595f0",
    "content": "go test fuzz v1\n[]byte(\"(SelectStmt (CommClause _ _))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/c7abb7fc60634bb8d57b5b7c225a6accf0d2eb56c88bfe5e44cdd3e0c3e29666",
    "content": "go test fuzz v1\n[]byte(\"(IfStmt _ cond@(BinaryExpr lhs op@(Or \\\"==\\\" \\\"!=\\\") (Builtin \\\"nil\\\")) _ _)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/ca92b01f6dbdcb91e335219081aa48c16893c217bf6edc020fcb78b3ebabcd1f",
    "content": "go test fuzz v1\n[]byte(\"(BinaryExpr (IntegerLiteral _) \\\"/\\\" (IntegerLiteral _))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/e027a03ee012e289def51d770ead1e8a136b60989d3d1fb9388a394da2f595da",
    "content": "go test fuzz v1\n[]byte(\"go test fuzz v1\\n[]byte(\\\"(CallExpr (Symbol \\\\\\\"(time.Time).Sub\\\\\\\") [(CallExpr (Symbol \\\\\\\"time.Now\\\\\\\") [])])\\\")\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/e1e59b9e6718f5089e98c955c391d38c7e243495ece9598826492ab734e5171f",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (Builtin \\\"append\\\") [_])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/e43aa6da655e6c326cfb1f8c9970b603411caf262af4a50980c5a5987ee696f3",
    "content": "go test fuzz v1\n[]byte(\"(IfStmt nil cond [(AssignStmt x@(Object _) \\\"=\\\" (Builtin b@(Or \\\"true\\\" \\\"false\\\")))] nil)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/e48990bfca21324ab7a29098b9a4b40fbd22bd5adcfa316b4b8af460a232b638",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (SelectorExpr (Ident \\\"strings\\\") (Ident \\\"EqualFold\\\")) [a b])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/eb0ecf0066fdafbe218a736d3fc071a52408311637cc527db239f110418e8616",
    "content": "go test fuzz v1\n[]byte(\"(StarExpr (UnaryExpr \\\"&\\\" _))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/ed6769b59df864327fba2b109f0cb965e5b8a6e5f1085e36f5635f1d65003a00",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t\\t(CallExpr\\n\\t\\t\\t(Ident \\\"copy\\\")\\n\\t\\t\\t[(SliceExpr slice nil limit nil)\\n\\t\\t\\t\\t(SliceExpr slice offset nil nil)])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/f640eee2b04d1b52793ba88998a86702893e23d2563d017be9be90efc04a43c6",
    "content": "go test fuzz v1\n[]byte(\"(Or (BasicLit \\\"INT\\\" _) (UnaryExpr (Or \\\"+\\\" \\\"-\\\") (IntegerLiteral _)))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/f855d335a52bd8b6ed4472abb33c0eb8f67a63d84f1c27398c23689fb2720645",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (SelectorExpr (Ident \\\"time\\\") (Ident \\\"Sleep\\\")) [arg])\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/fac160433f2d82b3c15a8c6ad3938fd85825a4f248108538938a57914e80f114",
    "content": "go test fuzz v1\n[]byte(\"(UnaryExpr \\\"!\\\" single@(UnaryExpr \\\"!\\\" x))\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/fb2c5ef5801f44e5bee94b82dbb1bc787cc4b7fbdb17e5cfcc4283f2c726a99f",
    "content": "go test fuzz v1\n[]byte(\"(CallExpr (SelectorExpr (Ident \\\"fmt\\\") (Ident \\\"Errorf\\\")) args)\")"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/fe6c6578776a5ce92474e943ac14979a308d4151d779fd4cfd782f7fb970165e",
    "content": "go test fuzz v1\n[]byte(\"(Or[a_a_a_a])\")\n"
  },
  {
    "path": "pattern/testdata/fuzz/FuzzParse/ff2017b5c630d7225812cfa8b29b6ebad665505492db847722ba79da5d2c89eb",
    "content": "go test fuzz v1\n[]byte(\"\\n\\t\\t(Or\\n\\t\\t\\t(AssignStmt _ (Or \\\">>=\\\" \\\"<<=\\\") _)\\n\\t\\t\\t(BinaryExpr _ (Or \\\">>\\\" \\\"<<\\\") _))\\n\\t\")"
  },
  {
    "path": "printf/fuzz.go",
    "content": "//go:build gofuzz\n\npackage printf\n\nfunc Fuzz(data []byte) int {\n\t_, err := Parse(string(data))\n\tif err == nil {\n\t\treturn 1\n\t}\n\treturn 0\n}\n"
  },
  {
    "path": "printf/printf.go",
    "content": "// Package printf implements a parser for fmt.Printf-style format\n// strings.\n//\n// It parses verbs according to the following syntax:\n//\n//\tNumeric -> '0'-'9'\n//\tLetter -> 'a'-'z' | 'A'-'Z'\n//\tIndex -> '[' Numeric+ ']'\n//\tStar -> '*'\n//\tStar -> Index '*'\n//\n//\tPrecision -> Numeric+ | Star\n//\tWidth -> Numeric+ | Star\n//\n//\tWidthAndPrecision -> Width '.' Precision\n//\tWidthAndPrecision -> Width '.'\n//\tWidthAndPrecision -> Width\n//\tWidthAndPrecision -> '.' Precision\n//\tWidthAndPrecision -> '.'\n//\n//\tFlag -> '+' | '-' | '#' | ' ' | '0'\n//\tVerb -> Letter | '%'\n//\n//\tInput -> '%' [ Flag+ ] [ WidthAndPrecision ] [ Index ] Verb\npackage printf\n\nimport (\n\t\"errors\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// ErrInvalid is returned for invalid format strings or verbs.\nvar ErrInvalid = errors.New(\"invalid format string\")\n\ntype Verb struct {\n\tLetter rune\n\tFlags  string\n\n\tWidth     Argument\n\tPrecision Argument\n\t// Which value in the argument list the verb uses.\n\t// -1 denotes the next argument,\n\t// values > 0 denote explicit arguments.\n\t// The value 0 denotes that no argument is consumed. This is the case for %%.\n\tValue int\n\n\tRaw string\n}\n\n// Argument is an implicit or explicit width or precision.\ntype Argument interface {\n\tisArgument()\n}\n\n// The Default value, when no width or precision is provided.\ntype Default struct{}\n\n// Zero is the implicit zero value.\n// This value may only appear for precisions in format strings like %6.f\ntype Zero struct{}\n\n// Star is a * value, which may either refer to the next argument (Index == -1) or an explicit argument.\ntype Star struct{ Index int }\n\n// A Literal value, such as 6 in %6d.\ntype Literal int\n\nfunc (Default) isArgument() {}\nfunc (Zero) isArgument()    {}\nfunc (Star) isArgument()    {}\nfunc (Literal) isArgument() {}\n\n// Parse parses f and returns a list of actions.\n// An action may either be a literal string, or a Verb.\nfunc Parse(f string) ([]any, error) {\n\tvar out []any\n\tfor len(f) > 0 {\n\t\tif f[0] == '%' {\n\t\t\tv, n, err := ParseVerb(f)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tf = f[n:]\n\t\t\tout = append(out, v)\n\t\t} else {\n\t\t\tn := strings.IndexByte(f, '%')\n\t\t\tif n > -1 {\n\t\t\t\tout = append(out, f[:n])\n\t\t\t\tf = f[n:]\n\t\t\t} else {\n\t\t\t\tout = append(out, f)\n\t\t\t\tf = \"\"\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out, nil\n}\n\nfunc atoi(s string) int {\n\tn, _ := strconv.Atoi(s)\n\treturn n\n}\n\n// ParseVerb parses the verb at the beginning of f.\n// It returns the verb, how much of the input was consumed, and an error, if any.\nfunc ParseVerb(f string) (Verb, int, error) {\n\tif len(f) < 2 {\n\t\treturn Verb{}, 0, ErrInvalid\n\t}\n\tconst (\n\t\tflags = 1\n\n\t\twidth      = 2\n\t\twidthStar  = 3\n\t\twidthIndex = 5\n\n\t\tdot       = 6\n\t\tprec      = 7\n\t\tprecStar  = 8\n\t\tprecIndex = 10\n\n\t\tverbIndex = 11\n\t\tverb      = 12\n\t)\n\n\tm := re.FindStringSubmatch(f)\n\tif m == nil {\n\t\treturn Verb{}, 0, ErrInvalid\n\t}\n\n\tv := Verb{\n\t\tLetter: []rune(m[verb])[0],\n\t\tFlags:  m[flags],\n\t\tRaw:    m[0],\n\t}\n\n\tif m[width] != \"\" {\n\t\t// Literal width\n\t\tv.Width = Literal(atoi(m[width]))\n\t} else if m[widthStar] != \"\" {\n\t\t// Star width\n\t\tif m[widthIndex] != \"\" {\n\t\t\tv.Width = Star{atoi(m[widthIndex])}\n\t\t} else {\n\t\t\tv.Width = Star{-1}\n\t\t}\n\t} else {\n\t\t// Default width\n\t\tv.Width = Default{}\n\t}\n\n\tif m[dot] == \"\" {\n\t\t// default precision\n\t\tv.Precision = Default{}\n\t} else {\n\t\tif m[prec] != \"\" {\n\t\t\t// Literal precision\n\t\t\tv.Precision = Literal(atoi(m[prec]))\n\t\t} else if m[precStar] != \"\" {\n\t\t\t// Star precision\n\t\t\tif m[precIndex] != \"\" {\n\t\t\t\tv.Precision = Star{atoi(m[precIndex])}\n\t\t\t} else {\n\t\t\t\tv.Precision = Star{-1}\n\t\t\t}\n\t\t} else {\n\t\t\t// Zero precision\n\t\t\tv.Precision = Zero{}\n\t\t}\n\t}\n\n\tif m[verb] == \"%\" {\n\t\tv.Value = 0\n\t} else if m[verbIndex] != \"\" {\n\t\tv.Value = atoi(m[verbIndex])\n\t} else {\n\t\tv.Value = -1\n\t}\n\n\treturn v, len(m[0]), nil\n}\n\nconst (\n\tflags             = `([+#0 -]*)`\n\tverb              = `([a-zA-Z%])`\n\tindex             = `(?:\\[([0-9]+)\\])`\n\tstar              = `((` + index + `)?\\*)`\n\twidth1            = `([0-9]+)`\n\twidth2            = star\n\twidth             = `(?:` + width1 + `|` + width2 + `)`\n\tprecision         = width\n\twidthAndPrecision = `(?:(?:` + width + `)?(?:(\\.)(?:` + precision + `)?)?)`\n)\n\nvar re = regexp.MustCompile(`^%` + flags + widthAndPrecision + `?` + index + `?` + verb)\n"
  },
  {
    "path": "printf/printf_test.go",
    "content": "package printf\n\nimport \"testing\"\n\nfunc BenchmarkParseVerb(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tParseVerb(\"%[3]*.[2]*[1]f\")\n\t}\n}\n\nfunc TestParseVerb(t *testing.T) {\n\tvar tests = []struct {\n\t\tin  string\n\t\tout Verb\n\t}{\n\t\t{\n\t\t\t`%d`,\n\t\t\tVerb{\n\t\t\t\tLetter:    'd',\n\t\t\t\tWidth:     Default{},\n\t\t\t\tPrecision: Default{},\n\t\t\t\tValue:     -1,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t`%#d`,\n\t\t\tVerb{\n\t\t\t\tLetter:    'd',\n\t\t\t\tFlags:     \"#\",\n\t\t\t\tWidth:     Default{},\n\t\t\t\tPrecision: Default{},\n\t\t\t\tValue:     -1,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t`%+#d`,\n\t\t\tVerb{\n\t\t\t\tLetter:    'd',\n\t\t\t\tFlags:     \"+#\",\n\t\t\t\tWidth:     Default{},\n\t\t\t\tPrecision: Default{},\n\t\t\t\tValue:     -1,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t`%[2]d`,\n\t\t\tVerb{\n\t\t\t\tLetter:    'd',\n\t\t\t\tWidth:     Default{},\n\t\t\t\tPrecision: Default{},\n\t\t\t\tValue:     2,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t`%[3]*.[2]*[1]f`,\n\t\t\tVerb{\n\t\t\t\tLetter:    'f',\n\t\t\t\tWidth:     Star{3},\n\t\t\t\tPrecision: Star{2},\n\t\t\t\tValue:     1,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t`%6.2f`,\n\t\t\tVerb{\n\t\t\t\tLetter:    'f',\n\t\t\t\tWidth:     Literal(6),\n\t\t\t\tPrecision: Literal(2),\n\t\t\t\tValue:     -1,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t`%#[1]x`,\n\t\t\tVerb{\n\t\t\t\tLetter:    'x',\n\t\t\t\tFlags:     \"#\",\n\t\t\t\tWidth:     Default{},\n\t\t\t\tPrecision: Default{},\n\t\t\t\tValue:     1,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"%%\",\n\t\t\tVerb{\n\t\t\t\tLetter:    '%',\n\t\t\t\tWidth:     Default{},\n\t\t\t\tPrecision: Default{},\n\t\t\t\tValue:     0,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"%*%\",\n\t\t\tVerb{\n\t\t\t\tLetter:    '%',\n\t\t\t\tWidth:     Star{Index: -1},\n\t\t\t\tPrecision: Default{},\n\t\t\t\tValue:     0,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"%[1]%\",\n\t\t\tVerb{\n\t\t\t\tLetter:    '%',\n\t\t\t\tWidth:     Default{},\n\t\t\t\tPrecision: Default{},\n\t\t\t\tValue:     0,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt.out.Raw = tt.in\n\t\tv, n, err := ParseVerb(tt.in)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"unexpected error %s while parsing %s\", err, tt.in)\n\t\t}\n\t\tif n != len(tt.in) {\n\t\t\tt.Errorf(\"ParseVerb only consumed %d of %d bytes\", n, len(tt.in))\n\t\t}\n\t\tif v != tt.out {\n\t\t\tt.Errorf(\"%s parsed to %#v, want %#v\", tt.in, v, tt.out)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "quickfix/analysis.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage quickfix\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/quickfix/qf1001\"\n\t\"honnef.co/go/tools/quickfix/qf1002\"\n\t\"honnef.co/go/tools/quickfix/qf1003\"\n\t\"honnef.co/go/tools/quickfix/qf1004\"\n\t\"honnef.co/go/tools/quickfix/qf1005\"\n\t\"honnef.co/go/tools/quickfix/qf1006\"\n\t\"honnef.co/go/tools/quickfix/qf1007\"\n\t\"honnef.co/go/tools/quickfix/qf1008\"\n\t\"honnef.co/go/tools/quickfix/qf1009\"\n\t\"honnef.co/go/tools/quickfix/qf1010\"\n\t\"honnef.co/go/tools/quickfix/qf1011\"\n\t\"honnef.co/go/tools/quickfix/qf1012\"\n)\n\nvar Analyzers = []*lint.Analyzer{\n\tqf1001.SCAnalyzer,\n\tqf1002.SCAnalyzer,\n\tqf1003.SCAnalyzer,\n\tqf1004.SCAnalyzer,\n\tqf1005.SCAnalyzer,\n\tqf1006.SCAnalyzer,\n\tqf1007.SCAnalyzer,\n\tqf1008.SCAnalyzer,\n\tqf1009.SCAnalyzer,\n\tqf1010.SCAnalyzer,\n\tqf1011.SCAnalyzer,\n\tqf1012.SCAnalyzer,\n}\n"
  },
  {
    "path": "quickfix/doc.go",
    "content": "//go:generate go run ../generate.go\n\n// Package quickfix contains analyzes that implement code refactorings.\n// None of these analyzers produce diagnostics that have to be followed.\n// Most of the time, they only provide alternative ways of doing things,\n// requiring users to make informed decisions.\n//\n// None of these analyzes should fail a build, and they are likely useless in CI as a whole.\npackage quickfix\n"
  },
  {
    "path": "quickfix/qf1001/qf1001.go",
    "content": "package qf1001\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"QF1001\",\n\t\tRun:      CheckDeMorgan,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    \"Apply De Morgan's law\",\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityHint,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar demorganQ = pattern.MustParse(`(UnaryExpr \"!\" expr@(BinaryExpr _ _ _))`)\n\nfunc CheckDeMorgan(pass *analysis.Pass) (any, error) {\n\t// TODO(dh): support going in the other direction, e.g. turning `!a && !b && !c` into `!(a || b || c)`\n\n\t// hasFloats reports whether any subexpression is of type float.\n\thasFloats := func(expr ast.Expr) bool {\n\t\tfound := false\n\t\tast.Inspect(expr, func(node ast.Node) bool {\n\t\t\tif expr, ok := node.(ast.Expr); ok {\n\t\t\t\tif typ := pass.TypesInfo.TypeOf(expr); typ != nil {\n\t\t\t\t\tif basic, ok := typ.Underlying().(*types.Basic); ok {\n\t\t\t\t\t\tif (basic.Info() & types.IsFloat) != 0 {\n\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t\treturn found\n\t}\n\n\tfor c := range code.Cursor(pass).Preorder((*ast.UnaryExpr)(nil)) {\n\t\tnode := c.Node()\n\t\tmatcher, ok := code.Match(pass, demorganQ, node)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\texpr := matcher.State[\"expr\"].(ast.Expr)\n\n\t\t// be extremely conservative when it comes to floats\n\t\tif hasFloats(expr) {\n\t\t\tcontinue\n\t\t}\n\n\t\tn := astutil.NegateDeMorgan(expr, false)\n\t\tnr := astutil.NegateDeMorgan(expr, true)\n\t\tnc, ok := astutil.CopyExpr(n)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tns := astutil.SimplifyParentheses(nc)\n\t\tnrc, ok := astutil.CopyExpr(nr)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tnrs := astutil.SimplifyParentheses(nrc)\n\n\t\tvar bn, bnr, bns, bnrs string\n\t\tswitch c.Parent().Node().(type) {\n\t\tcase *ast.BinaryExpr, *ast.IfStmt, *ast.ForStmt, *ast.SwitchStmt:\n\t\t\t// Always add parentheses for if, for and switch. If\n\t\t\t// they're unnecessary, go/printer will strip them when\n\t\t\t// the whole file gets formatted.\n\n\t\t\tbn = report.Render(pass, &ast.ParenExpr{X: n})\n\t\t\tbnr = report.Render(pass, &ast.ParenExpr{X: nr})\n\t\t\tbns = report.Render(pass, &ast.ParenExpr{X: ns})\n\t\t\tbnrs = report.Render(pass, &ast.ParenExpr{X: nrs})\n\n\t\tdefault:\n\t\t\t// TODO are there other types where we don't want to strip parentheses?\n\t\t\tbn = report.Render(pass, n)\n\t\t\tbnr = report.Render(pass, nr)\n\t\t\tbns = report.Render(pass, ns)\n\t\t\tbnrs = report.Render(pass, nrs)\n\t\t}\n\n\t\t// Note: we cannot compare the ASTs directly, because\n\t\t// simplifyParentheses might have rebalanced trees without\n\t\t// affecting the rendered form.\n\t\tvar fixes []analysis.SuggestedFix\n\t\tfixes = append(fixes, edit.Fix(\"Apply De Morgan's law\", edit.ReplaceWithString(node, bn)))\n\t\tif bn != bns {\n\t\t\tfixes = append(fixes, edit.Fix(\"Apply De Morgan's law & simplify\", edit.ReplaceWithString(node, bns)))\n\t\t}\n\t\tif bn != bnr {\n\t\t\tfixes = append(fixes, edit.Fix(\"Apply De Morgan's law recursively\", edit.ReplaceWithString(node, bnr)))\n\t\t\tif bnr != bnrs {\n\t\t\t\tfixes = append(fixes, edit.Fix(\"Apply De Morgan's law recursively & simplify\", edit.ReplaceWithString(node, bnrs)))\n\t\t\t}\n\t\t}\n\n\t\treport.Report(pass, node, \"could apply De Morgan's law\", report.Fixes(fixes...))\n\t}\n\n\treturn nil, nil\n}\n"
  },
  {
    "path": "quickfix/qf1001/qf1001_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1001\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1001/testdata/go1.0/CheckDeMorgan/CheckDeMorgan.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar a, b, c bool\n\tvar e, f, g int\n\tvar h, i float64\n\n\t_ = !(a && b && (!c || e > f) && g == f) //@ diag(`could apply De Morgan's law`)\n\t_ = !(a && h > i)\n}\n"
  },
  {
    "path": "quickfix/qf1001/testdata/go1.0/CheckDeMorgan/CheckDeMorgan.go.golden",
    "content": "-- Apply De Morgan's law --\npackage pkg\n\nfunc fn() {\n\tvar a, b, c bool\n\tvar e, f, g int\n\tvar h, i float64\n\n\t_ = !a || !b || !(!c || e > f) || g != f //@ diag(`could apply De Morgan's law`)\n\t_ = !(a && h > i)\n}\n\n-- Apply De Morgan's law recursively --\npackage pkg\n\nfunc fn() {\n\tvar a, b, c bool\n\tvar e, f, g int\n\tvar h, i float64\n\n\t_ = !a || !b || (c && e <= f) || g != f //@ diag(`could apply De Morgan's law`)\n\t_ = !(a && h > i)\n}\n\n-- Apply De Morgan's law recursively & simplify --\npackage pkg\n\nfunc fn() {\n\tvar a, b, c bool\n\tvar e, f, g int\n\tvar h, i float64\n\n\t_ = !a || !b || c && e <= f || g != f //@ diag(`could apply De Morgan's law`)\n\t_ = !(a && h > i)\n}\n"
  },
  {
    "path": "quickfix/qf1001/testdata/go1.0/CheckDeMorgan/kvexpr.go",
    "content": "package pkg\n\nfunc do() bool {\n\ttype Info struct {\n\t\tidx int\n\t}\n\n\tvar state map[Info]int\n\t// Don't crash on KeyValueExpr\n\treturn !(state[Info{idx: 6}] == 6 || false) //@ diag(`could apply De Morgan's law`)\n}\n"
  },
  {
    "path": "quickfix/qf1001/testdata/go1.0/CheckDeMorgan/kvexpr.go.golden",
    "content": "-- Apply De Morgan's law --\npackage pkg\n\nfunc do() bool {\n\ttype Info struct {\n\t\tidx int\n\t}\n\n\tvar state map[Info]int\n\t// Don't crash on KeyValueExpr\n\treturn state[Info{idx: 6}] != 6 && !false //@ diag(`could apply De Morgan's law`)\n}\n"
  },
  {
    "path": "quickfix/qf1002/qf1002.go",
    "content": "package qf1002\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"QF1002\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Convert untagged switch to tagged switch\",\n\t\tText: `\nAn untagged switch that compares a single variable against a series of\nvalues can be replaced with a tagged switch.`,\n\t\tBefore: `\nswitch {\ncase x == 1 || x == 2, x == 3:\n    ...\ncase x == 4:\n    ...\ndefault:\n    ...\n}`,\n\n\t\tAfter: `\nswitch x {\ncase 1, 2, 3:\n    ...\ncase 4:\n    ...\ndefault:\n    ...\n}`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityHint,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tswtch := node.(*ast.SwitchStmt)\n\t\tif swtch.Tag != nil || len(swtch.Body.List) == 0 {\n\t\t\treturn\n\t\t}\n\n\t\tpairs := make([][]*ast.BinaryExpr, len(swtch.Body.List))\n\t\tfor i, stmt := range swtch.Body.List {\n\t\t\tstmt := stmt.(*ast.CaseClause)\n\t\t\tfor _, cond := range stmt.List {\n\t\t\t\tif !findSwitchPairs(pass, cond, &pairs[i]) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvar x ast.Expr\n\t\tfor _, pair := range pairs {\n\t\t\tif len(pair) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif x == nil {\n\t\t\t\tx = pair[0].X\n\t\t\t} else {\n\t\t\t\tif !astutil.Equal(x, pair[0].X) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif x == nil {\n\t\t\t// the switch only has a default case\n\t\t\tif len(pairs) > 1 {\n\t\t\t\tpanic(\"found more than one case clause with no pairs\")\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tedits := make([]analysis.TextEdit, 0, len(swtch.Body.List)+1)\n\t\tfor i, stmt := range swtch.Body.List {\n\t\t\tstmt := stmt.(*ast.CaseClause)\n\t\t\tif stmt.List == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tvar values []string\n\t\t\tfor _, binexpr := range pairs[i] {\n\t\t\t\ty := binexpr.Y\n\t\t\t\tif p, ok := y.(*ast.ParenExpr); ok {\n\t\t\t\t\ty = p.X\n\t\t\t\t}\n\t\t\t\tvalues = append(values, report.Render(pass, y))\n\t\t\t}\n\n\t\t\tedits = append(edits, edit.ReplaceWithString(edit.Range{stmt.List[0].Pos(), stmt.Colon}, strings.Join(values, \", \")))\n\t\t}\n\t\tpos := swtch.Body.Lbrace\n\t\tedits = append(edits, edit.ReplaceWithString(edit.Range{pos, pos}, \" \"+report.Render(pass, x)))\n\t\treport.Report(pass, swtch, fmt.Sprintf(\"could use tagged switch on %s\", report.Render(pass, x)),\n\t\t\treport.Fixes(edit.Fix(\"Replace with tagged switch\", edits...)),\n\t\t\treport.ShortRange())\n\t}\n\n\tcode.Preorder(pass, fn, (*ast.SwitchStmt)(nil))\n\treturn nil, nil\n}\n\nfunc findSwitchPairs(pass *analysis.Pass, expr ast.Expr, pairs *[]*ast.BinaryExpr) bool {\n\tbinexpr, ok := astutil.Unparen(expr).(*ast.BinaryExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\tswitch binexpr.Op {\n\tcase token.EQL:\n\t\tif code.MayHaveSideEffects(pass, binexpr.X, nil) || code.MayHaveSideEffects(pass, binexpr.Y, nil) {\n\t\t\treturn false\n\t\t}\n\t\t// syntactic identity should suffice. we do not allow side\n\t\t// effects in the case clauses, so there should be no way for\n\t\t// values to change.\n\t\tif len(*pairs) > 0 && !astutil.Equal(binexpr.X, (*pairs)[0].X) {\n\t\t\treturn false\n\t\t}\n\t\t*pairs = append(*pairs, binexpr)\n\t\treturn true\n\tcase token.LOR:\n\t\treturn findSwitchPairs(pass, binexpr.X, pairs) && findSwitchPairs(pass, binexpr.Y, pairs)\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "quickfix/qf1002/qf1002_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1002\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1002/testdata/go1.0/CheckTaglessSwitch/CheckTaglessSwitch.go",
    "content": "package pkg\n\nfunc foo() int { return 0 }\n\nfunc fn1() {\n\tvar x, y int\n\tvar z map[string][]int\n\tvar a bool\n\n\tswitch { //@ diag(`could use tagged switch on x`)\n\tcase x == 4: // comment\n\tcase x == 1 || x == 2, x == 3:\n\t}\n\n\tswitch { //@ diag(`could use tagged switch on x`)\n\tcase x == 1 || x == 2, x == 3:\n\tcase x == 4:\n\tdefault:\n\t}\n\n\tswitch { //@ diag(`could use tagged switch on z[\"\"][0]`)\n\tcase z[\"\"][0] == 1 || z[\"\"][0] == 2:\n\t}\n\n\tswitch { //@ diag(`could use tagged switch on a`)\n\tcase a == (x == y) || a == (x != y):\n\t}\n\n\tswitch {\n\tcase z[\"\"][0] == 1 || z[\"\"][1] == 2:\n\t}\n\n\tswitch {\n\tcase x == 1 || x == 2, y == 3:\n\tcase x == 4:\n\tdefault:\n\t}\n\n\tswitch {\n\tcase x == 1 || x == 2, x == 3:\n\tcase y == 4:\n\t}\n\n\tswitch {\n\tcase x == 1 || x == 2, x == foo():\n\tcase x == 4:\n\tdefault:\n\t}\n\n\tswitch {\n\t}\n\n\tswitch {\n\tdefault:\n\t}\n\n\tswitch {\n\tcase x == 1 && x == 2:\n\t}\n\n\tswitch b := 42; { //@ diag(`could use tagged switch on b`)\n\tcase b == 0:\n\tcase b == 1:\n\tdefault:\n\t\t_ = b\n\t}\n}\n"
  },
  {
    "path": "quickfix/qf1002/testdata/go1.0/CheckTaglessSwitch/CheckTaglessSwitch.go.golden",
    "content": "package pkg\n\nfunc foo() int { return 0 }\n\nfunc fn1() {\n\tvar x, y int\n\tvar z map[string][]int\n\tvar a bool\n\n\tswitch x { //@ diag(`could use tagged switch on x`)\n\tcase 4: // comment\n\tcase 1, 2, 3:\n\t}\n\n\tswitch x { //@ diag(`could use tagged switch on x`)\n\tcase 1, 2, 3:\n\tcase 4:\n\tdefault:\n\t}\n\n\tswitch z[\"\"][0] { //@ diag(`could use tagged switch on z[\"\"][0]`)\n\tcase 1, 2:\n\t}\n\n\tswitch a { //@ diag(`could use tagged switch on a`)\n\tcase x == y, x != y:\n\t}\n\n\tswitch {\n\tcase z[\"\"][0] == 1 || z[\"\"][1] == 2:\n\t}\n\n\tswitch {\n\tcase x == 1 || x == 2, y == 3:\n\tcase x == 4:\n\tdefault:\n\t}\n\n\tswitch {\n\tcase x == 1 || x == 2, x == 3:\n\tcase y == 4:\n\t}\n\n\tswitch {\n\tcase x == 1 || x == 2, x == foo():\n\tcase x == 4:\n\tdefault:\n\t}\n\n\tswitch {\n\t}\n\n\tswitch {\n\tdefault:\n\t}\n\n\tswitch {\n\tcase x == 1 && x == 2:\n\t}\n\n\tswitch b := 42; b { //@ diag(`could use tagged switch on b`)\n\tcase 0:\n\tcase 1:\n\tdefault:\n\t\t_ = b\n\t}\n}\n"
  },
  {
    "path": "quickfix/qf1003/qf1003.go",
    "content": "package qf1003\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"QF1003\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Convert if/else-if chain to tagged switch\",\n\t\tText: `\nA series of if/else-if checks comparing the same variable against\nvalues can be replaced with a tagged switch.`,\n\t\tBefore: `\nif x == 1 || x == 2 {\n    ...\n} else if x == 3 {\n    ...\n} else {\n    ...\n}`,\n\n\t\tAfter: `\nswitch x {\ncase 1, 2:\n    ...\ncase 3:\n    ...\ndefault:\n    ...\n}`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityInfo,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\nnodeLoop:\n\tfor c := range code.Cursor(pass).Preorder((*ast.IfStmt)(nil)) {\n\t\tnode := c.Node()\n\t\tif _, ok := c.Parent().Node().(*ast.IfStmt); ok {\n\t\t\t// this if statement is part of an if-else chain\n\t\t\tcontinue\n\t\t}\n\t\tifstmt := node.(*ast.IfStmt)\n\n\t\tm := map[ast.Expr][]*ast.BinaryExpr{}\n\t\tfor item := ifstmt; item != nil; {\n\t\t\tif item.Init != nil {\n\t\t\t\tcontinue nodeLoop\n\t\t\t}\n\t\t\tif item.Body == nil {\n\t\t\t\tcontinue nodeLoop\n\t\t\t}\n\n\t\t\tskip := false\n\t\t\tast.Inspect(item.Body, func(node ast.Node) bool {\n\t\t\t\tif branch, ok := node.(*ast.BranchStmt); ok && branch.Tok != token.GOTO {\n\t\t\t\t\tskip = true\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t\tif skip {\n\t\t\t\tcontinue nodeLoop\n\t\t\t}\n\n\t\t\tvar pairs []*ast.BinaryExpr\n\t\t\tif !findSwitchPairs(pass, item.Cond, &pairs) {\n\t\t\t\tcontinue nodeLoop\n\t\t\t}\n\t\t\tm[item.Cond] = pairs\n\t\t\tswitch els := item.Else.(type) {\n\t\t\tcase *ast.IfStmt:\n\t\t\t\titem = els\n\t\t\tcase *ast.BlockStmt, nil:\n\t\t\t\titem = nil\n\t\t\tdefault:\n\t\t\t\tpanic(fmt.Sprintf(\"unreachable: %T\", els))\n\t\t\t}\n\t\t}\n\n\t\tvar x ast.Expr\n\t\tfor _, pair := range m {\n\t\t\tif len(pair) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif x == nil {\n\t\t\t\tx = pair[0].X\n\t\t\t} else {\n\t\t\t\tif !astutil.Equal(x, pair[0].X) {\n\t\t\t\t\tcontinue nodeLoop\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif x == nil {\n\t\t\t// shouldn't happen\n\t\t\tcontinue nodeLoop\n\t\t}\n\n\t\t// We require at least two 'if' to make this suggestion, to\n\t\t// avoid clutter in the editor.\n\t\tif len(m) < 2 {\n\t\t\tcontinue nodeLoop\n\t\t}\n\n\t\t// Note that we insert the switch statement as the first text edit instead of the last one so that gopls has an\n\t\t// easier time converting it to an LSP-conforming edit.\n\t\t//\n\t\t// Specifically:\n\t\t// > Text edits ranges must never overlap, that means no part of the original\n\t\t// > document must be manipulated by more than one edit. However, it is\n\t\t// > possible that multiple edits have the same start position: multiple\n\t\t// > inserts, or any number of inserts followed by a single remove or replace\n\t\t// > edit. If multiple inserts have the same position, the order in the array\n\t\t// > defines the order in which the inserted strings appear in the resulting\n\t\t// > text.\n\t\t//\n\t\t// See https://go.dev/issue/63930\n\t\t//\n\t\t// FIXME this edit forces the first case to begin in column 0 because we ignore indentation. try to fix that.\n\t\tedits := []analysis.TextEdit{edit.ReplaceWithString(edit.Range{ifstmt.If, ifstmt.If}, fmt.Sprintf(\"switch %s {\\n\", report.Render(pass, x)))}\n\t\tfor item := ifstmt; item != nil; {\n\t\t\tvar end token.Pos\n\t\t\tif item.Else != nil {\n\t\t\t\tend = item.Else.Pos()\n\t\t\t} else {\n\t\t\t\t// delete up to but not including the closing brace.\n\t\t\t\tend = item.Body.Rbrace\n\t\t\t}\n\n\t\t\tvar conds []string\n\t\t\tfor _, cond := range m[item.Cond] {\n\t\t\t\ty := cond.Y\n\t\t\t\tif p, ok := y.(*ast.ParenExpr); ok {\n\t\t\t\t\ty = p.X\n\t\t\t\t}\n\t\t\t\tconds = append(conds, report.Render(pass, y))\n\t\t\t}\n\t\t\tsconds := strings.Join(conds, \", \")\n\t\t\tedits = append(edits,\n\t\t\t\tedit.ReplaceWithString(edit.Range{item.If, item.Body.Lbrace + 1}, \"case \"+sconds+\":\"),\n\t\t\t\tedit.Delete(edit.Range{item.Body.Rbrace, end}))\n\n\t\t\tswitch els := item.Else.(type) {\n\t\t\tcase *ast.IfStmt:\n\t\t\t\titem = els\n\t\t\tcase *ast.BlockStmt:\n\t\t\t\tedits = append(edits, edit.ReplaceWithString(edit.Range{els.Lbrace, els.Lbrace + 1}, \"default:\"))\n\t\t\t\titem = nil\n\t\t\tcase nil:\n\t\t\t\titem = nil\n\t\t\tdefault:\n\t\t\t\tpanic(fmt.Sprintf(\"unreachable: %T\", els))\n\t\t\t}\n\t\t}\n\t\treport.Report(pass, ifstmt, fmt.Sprintf(\"could use tagged switch on %s\", report.Render(pass, x)),\n\t\t\treport.Fixes(edit.Fix(\"Replace with tagged switch\", edits...)),\n\t\t\treport.ShortRange())\n\t}\n\treturn nil, nil\n}\n\nfunc findSwitchPairs(pass *analysis.Pass, expr ast.Expr, pairs *[]*ast.BinaryExpr) bool {\n\tbinexpr, ok := astutil.Unparen(expr).(*ast.BinaryExpr)\n\tif !ok {\n\t\treturn false\n\t}\n\tswitch binexpr.Op {\n\tcase token.EQL:\n\t\tif code.MayHaveSideEffects(pass, binexpr.X, nil) || code.MayHaveSideEffects(pass, binexpr.Y, nil) {\n\t\t\treturn false\n\t\t}\n\t\t// syntactic identity should suffice. we do not allow side\n\t\t// effects in the case clauses, so there should be no way for\n\t\t// values to change.\n\t\tif len(*pairs) > 0 && !astutil.Equal(binexpr.X, (*pairs)[0].X) {\n\t\t\treturn false\n\t\t}\n\t\t*pairs = append(*pairs, binexpr)\n\t\treturn true\n\tcase token.LOR:\n\t\treturn findSwitchPairs(pass, binexpr.X, pairs) && findSwitchPairs(pass, binexpr.Y, pairs)\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "quickfix/qf1003/qf1003_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1003\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1003/testdata/go1.0/CheckIfElseToSwitch/CheckIfElseToSwitch.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar x, y int\n\tvar z []int\n\tvar a bool\n\n\tif x == 1 || x == 2 { //@ diag(`could use tagged switch on x`)\n\t} else if x == 3 {\n\t}\n\n\tif x == 1 || x == 2 { //@ diag(`could use tagged switch on x`)\n\t} else if x == 3 {\n\t} else {\n\t}\n\n\tif x == 1 || x == 2 {\n\t} else if y == 3 {\n\t} else {\n\t}\n\n\tif a == (x == y) { //@ diag(`could use tagged switch on a`)\n\t} else if a == (x != y) {\n\t}\n\n\tif z[0] == 1 || z[0] == 2 { //@ diag(`could use tagged switch on z[0]`)\n\t} else if z[0] == 3 {\n\t}\n\n\tfor {\n\t\tif x == 1 || x == 2 { //@ diag(`could use tagged switch on x`)\n\t\t} else if x == 3 {\n\t\t}\n\t}\n\n\tfor {\n\t\tif x == 1 || x == 2 {\n\t\t} else if x == 3 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif x == 1 || x == 2 {\n\t}\n}\n"
  },
  {
    "path": "quickfix/qf1003/testdata/go1.0/CheckIfElseToSwitch/CheckIfElseToSwitch.go.golden",
    "content": "package pkg\n\nfunc fn() {\n\tvar x, y int\n\tvar z []int\n\tvar a bool\n\n\tswitch x {\n\tcase 1, 2: //@ diag(`could use tagged switch on x`)\n\tcase 3:\n\t}\n\n\tswitch x {\n\tcase 1, 2: //@ diag(`could use tagged switch on x`)\n\tcase 3:\n\tdefault:\n\t}\n\n\tif x == 1 || x == 2 {\n\t} else if y == 3 {\n\t} else {\n\t}\n\n\tswitch a {\n\tcase x == y: //@ diag(`could use tagged switch on a`)\n\tcase x != y:\n\t}\n\n\tswitch z[0] {\n\tcase 1, 2: //@ diag(`could use tagged switch on z[0]`)\n\tcase 3:\n\t}\n\n\tfor {\n\t\tswitch x {\n\t\tcase 1, 2: //@ diag(`could use tagged switch on x`)\n\t\tcase 3:\n\t\t}\n\t}\n\n\tfor {\n\t\tif x == 1 || x == 2 {\n\t\t} else if x == 3 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif x == 1 || x == 2 {\n\t}\n}\n"
  },
  {
    "path": "quickfix/qf1004/qf1004.go",
    "content": "package qf1004\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\ttypeindexanalyzer \"honnef.co/go/tools/internal/analysisinternal/typeindex\"\n\t\"honnef.co/go/tools/internal/typesinternal/typeindex\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"QF1004\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{typeindexanalyzer.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Use \\'strings.ReplaceAll\\' instead of \\'strings.Replace\\' with \\'n == -1\\'`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityHint,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar fns = []struct {\n\tpath        string\n\tname        string\n\treplacement string\n}{\n\t{\"strings\", \"Replace\", \"strings.ReplaceAll\"},\n\t{\"strings\", \"SplitN\", \"strings.Split\"},\n\t{\"strings\", \"SplitAfterN\", \"strings.SplitAfter\"},\n\t{\"bytes\", \"Replace\", \"bytes.ReplaceAll\"},\n\t{\"bytes\", \"SplitN\", \"bytes.Split\"},\n\t{\"bytes\", \"SplitAfterN\", \"bytes.SplitAfter\"},\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// XXX respect minimum Go version\n\n\t// FIXME(dh): create proper suggested fix for renamed import\n\n\tindex := pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index)\n\tfor _, fn := range fns {\n\t\tfor c := range index.Calls(index.Object(fn.path, fn.name)) {\n\t\t\tcall := c.Node().(*ast.CallExpr)\n\t\t\tif op, ok := call.Args[len(call.Args)-1].(*ast.UnaryExpr); ok && op.Op == token.SUB {\n\t\t\t\tif lit, ok := op.X.(*ast.BasicLit); ok && lit.Value == \"1\" {\n\t\t\t\t\treport.Report(pass, call.Fun, fmt.Sprintf(\"could use %s instead\", fn.replacement),\n\t\t\t\t\t\treport.Fixes(edit.Fix(fmt.Sprintf(\"Use %s instead\", fn.replacement),\n\t\t\t\t\t\t\tedit.ReplaceWithString(call.Fun, fn.replacement),\n\t\t\t\t\t\t\tedit.Delete(op))))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "quickfix/qf1004/qf1004_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1004\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1004/testdata/go1.0/CheckStringsReplaceAll/CheckStringsReplaceAll.go",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n)\n\nfunc fn() {\n\tstrings.Replace(\"\", \"\", \"\", -1) //@ diag(`could use strings.ReplaceAll instead`)\n\tstrings.Replace(\"\", \"\", \"\", 0)\n\tstrings.Replace(\"\", \"\", \"\", 1)\n\n\tstrings.SplitN(\"\", \"\", -1) //@ diag(`could use strings.Split instead`)\n\tstrings.SplitN(\"\", \"\", 0)\n\tstrings.SplitN(\"\", \"\", 1)\n\n\tstrings.SplitAfterN(\"\", \"\", -1) //@ diag(`could use strings.SplitAfter instead`)\n\tstrings.SplitAfterN(\"\", \"\", 0)\n\tstrings.SplitAfterN(\"\", \"\", 1)\n\n\tbytes.Replace(nil, nil, nil, -1) //@ diag(`could use bytes.ReplaceAll instead`)\n\tbytes.Replace(nil, nil, nil, 0)\n\tbytes.Replace(nil, nil, nil, 1)\n\n\tbytes.SplitN(nil, nil, -1) //@ diag(`could use bytes.Split instead`)\n\tbytes.SplitN(nil, nil, 0)\n\tbytes.SplitN(nil, nil, 1)\n\n\tbytes.SplitAfterN(nil, nil, -1) //@ diag(`could use bytes.SplitAfter instead`)\n\tbytes.SplitAfterN(nil, nil, 0)\n\tbytes.SplitAfterN(nil, nil, 1)\n}\n"
  },
  {
    "path": "quickfix/qf1004/testdata/go1.0/CheckStringsReplaceAll/CheckStringsReplaceAll.go.golden",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n)\n\nfunc fn() {\n\tstrings.ReplaceAll(\"\", \"\", \"\") //@ diag(`could use strings.ReplaceAll instead`)\n\tstrings.Replace(\"\", \"\", \"\", 0)\n\tstrings.Replace(\"\", \"\", \"\", 1)\n\n\tstrings.Split(\"\", \"\") //@ diag(`could use strings.Split instead`)\n\tstrings.SplitN(\"\", \"\", 0)\n\tstrings.SplitN(\"\", \"\", 1)\n\n\tstrings.SplitAfter(\"\", \"\") //@ diag(`could use strings.SplitAfter instead`)\n\tstrings.SplitAfterN(\"\", \"\", 0)\n\tstrings.SplitAfterN(\"\", \"\", 1)\n\n\tbytes.ReplaceAll(nil, nil, nil) //@ diag(`could use bytes.ReplaceAll instead`)\n\tbytes.Replace(nil, nil, nil, 0)\n\tbytes.Replace(nil, nil, nil, 1)\n\n\tbytes.Split(nil, nil) //@ diag(`could use bytes.Split instead`)\n\tbytes.SplitN(nil, nil, 0)\n\tbytes.SplitN(nil, nil, 1)\n\n\tbytes.SplitAfter(nil, nil) //@ diag(`could use bytes.SplitAfter instead`)\n\tbytes.SplitAfterN(nil, nil, 0)\n\tbytes.SplitAfterN(nil, nil, 1)\n}\n"
  },
  {
    "path": "quickfix/qf1005/qf1005.go",
    "content": "package qf1005\n\nimport (\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"QF1005\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Expand call to \\'math.Pow\\'`,\n\t\tText:     `Some uses of \\'math.Pow\\' can be simplified to basic multiplication.`,\n\t\tBefore:   `math.Pow(x, 2)`,\n\t\tAfter:    `x * x`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityHint,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar mathPowQ = pattern.MustParse(`(CallExpr (Symbol \"math.Pow\") [x (IntegerLiteral n)])`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, matcher := range code.Matches(pass, mathPowQ) {\n\t\tx := matcher.State[\"x\"].(ast.Expr)\n\t\tif code.MayHaveSideEffects(pass, x, nil) {\n\t\t\tcontinue\n\t\t}\n\t\tn, ok := constant.Int64Val(constant.ToInt(matcher.State[\"n\"].(types.TypeAndValue).Value))\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tneedConversion := false\n\t\tif T, ok := pass.TypesInfo.Types[x]; ok && T.Value != nil {\n\t\t\tinfo := types.Info{\n\t\t\t\tTypes: map[ast.Expr]types.TypeAndValue{},\n\t\t\t}\n\n\t\t\t// determine if the constant expression would have type float64 if used on its own\n\t\t\tif err := types.CheckExpr(pass.Fset, pass.Pkg, x.Pos(), x, &info); err != nil {\n\t\t\t\t// This should not happen\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif T, ok := info.Types[x].Type.(*types.Basic); ok {\n\t\t\t\tif T.Kind() != types.UntypedFloat && T.Kind() != types.Float64 {\n\t\t\t\t\tneedConversion = true\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tneedConversion = true\n\t\t\t}\n\t\t}\n\n\t\tvar replacement ast.Expr\n\t\tswitch n {\n\t\tcase 0:\n\t\t\treplacement = &ast.BasicLit{\n\t\t\t\tKind:  token.FLOAT,\n\t\t\t\tValue: \"1.0\",\n\t\t\t}\n\t\tcase 1:\n\t\t\treplacement = x\n\t\tcase 2, 3:\n\t\t\tr := &ast.BinaryExpr{\n\t\t\t\tX:  x,\n\t\t\t\tOp: token.MUL,\n\t\t\t\tY:  x,\n\t\t\t}\n\t\t\tfor i := 3; i <= int(n); i++ {\n\t\t\t\tr = &ast.BinaryExpr{\n\t\t\t\t\tX:  r,\n\t\t\t\t\tOp: token.MUL,\n\t\t\t\t\tY:  x,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trc, ok := astutil.CopyExpr(r)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treplacement = astutil.SimplifyParentheses(rc)\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\t\tif needConversion && n != 0 {\n\t\t\treplacement = &ast.CallExpr{\n\t\t\t\tFun:  &ast.Ident{Name: \"float64\"},\n\t\t\t\tArgs: []ast.Expr{replacement},\n\t\t\t}\n\t\t}\n\t\treport.Report(pass, node, \"could expand call to math.Pow\",\n\t\t\treport.Fixes(edit.Fix(\"Expand call to math.Pow\", edit.ReplaceWithNode(pass.Fset, node, replacement))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "quickfix/qf1005/qf1005_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1005\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1005/testdata/go1.0/CheckMathPow/CheckMathPow.go",
    "content": "package pkg\n\nimport \"math\"\n\nfunc fn() {\n\tvar x float64\n\n\t_ = math.Pow(x, 0) //@ diag(`could expand call to math.Pow`)\n\t_ = math.Pow(x, 1) //@ diag(`could expand call to math.Pow`)\n\t_ = math.Pow(x, 2) //@ diag(`could expand call to math.Pow`)\n\t_ = math.Pow(x, 3) //@ diag(`could expand call to math.Pow`)\n\t_ = math.Pow(x, 6)\n\n\tconst a = 2\n\tconst b = 2.0\n\tconst c float64 = 2\n\n\t_ = math.Pow(a, 2)     //@ diag(`could expand call to math.Pow`)\n\t_ = math.Pow(b, 2)     //@ diag(`could expand call to math.Pow`)\n\t_ = math.Pow(c, 2)     //@ diag(`could expand call to math.Pow`)\n\t_ = math.Pow(a*1.0, 2) //@ diag(`could expand call to math.Pow`)\n\n\t_ = math.Pow(x*2, 2) //@ diag(`could expand call to math.Pow`)\n\t_ = math.Pow(x+2, 2) //@ diag(`could expand call to math.Pow`)\n\n\t_ = math.Pow(x, x)\n\t_ = math.Pow(x, -1)\n}\n"
  },
  {
    "path": "quickfix/qf1005/testdata/go1.0/CheckMathPow/CheckMathPow.go.golden",
    "content": "package pkg\n\nimport \"math\"\n\nfunc fn() {\n\tvar x float64\n\n\t_ = 1.0       //@ diag(`could expand call to math.Pow`)\n\t_ = x         //@ diag(`could expand call to math.Pow`)\n\t_ = x * x     //@ diag(`could expand call to math.Pow`)\n\t_ = x * x * x //@ diag(`could expand call to math.Pow`)\n\t_ = math.Pow(x, 6)\n\n\tconst a = 2\n\tconst b = 2.0\n\tconst c float64 = 2\n\n\t_ = float64(a * a)    //@ diag(`could expand call to math.Pow`)\n\t_ = b * b             //@ diag(`could expand call to math.Pow`)\n\t_ = c * c             //@ diag(`could expand call to math.Pow`)\n\t_ = a * 1.0 * a * 1.0 //@ diag(`could expand call to math.Pow`)\n\n\t_ = x * 2 * x * 2     //@ diag(`could expand call to math.Pow`)\n\t_ = (x + 2) * (x + 2) //@ diag(`could expand call to math.Pow`)\n\n\t_ = math.Pow(x, x)\n\t_ = math.Pow(x, -1)\n}\n"
  },
  {
    "path": "quickfix/qf1006/qf1006.go",
    "content": "package qf1006\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"QF1006\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Lift \\'if\\'+\\'break\\' into loop condition`,\n\t\tBefore: `\nfor {\n    if done {\n        break\n    }\n    ...\n}`,\n\n\t\tAfter: `\nfor !done {\n    ...\n}`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityHint,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkForLoopIfBreak = pattern.MustParse(`(ForStmt nil nil nil if@(IfStmt nil cond (BranchStmt \"BREAK\" nil) nil):_)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkForLoopIfBreak) {\n\t\tpos := node.Pos() + token.Pos(len(\"for\"))\n\t\tr := astutil.NegateDeMorgan(m.State[\"cond\"].(ast.Expr), false)\n\n\t\t// FIXME(dh): we're leaving behind an empty line when we\n\t\t// delete the old if statement. However, we can't just delete\n\t\t// an additional character, in case there closing curly brace\n\t\t// is followed by a comment, or Windows newlines.\n\t\treport.Report(pass, m.State[\"if\"].(ast.Node), \"could lift into loop condition\",\n\t\t\treport.Fixes(edit.Fix(\"Lift into loop condition\",\n\t\t\t\tedit.ReplaceWithString(edit.Range{pos, pos}, \" \"+report.Render(pass, r)),\n\t\t\t\tedit.Delete(m.State[\"if\"].(ast.Node)))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "quickfix/qf1006/qf1006_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1006\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1006/testdata/go1.0/CheckForLoopIfBreak/CheckForLoopIfBreak.go",
    "content": "package pkg\n\nfunc done() bool { return false }\n\nvar a, b int\nvar x bool\n\nfunc fn() {\n\tfor {\n\t\tif done() { //@ diag(`could lift into loop condition`)\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor {\n\t\tif !done() { //@ diag(`could lift into loop condition`)\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor {\n\t\tif a > b || b > a { //@ diag(`could lift into loop condition`)\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor {\n\t\tif x && (a == b) { //@ diag(`could lift into loop condition`)\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor {\n\t\tif done() { //@ diag(`could lift into loop condition`)\n\t\t\tbreak\n\t\t}\n\t\tprintln()\n\t}\n\n\tfor {\n\t\tprintln()\n\t\tif done() {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor {\n\t\tif done() {\n\t\t\tprintln()\n\t\t\tbreak\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "quickfix/qf1006/testdata/go1.0/CheckForLoopIfBreak/CheckForLoopIfBreak.go.golden",
    "content": "package pkg\n\nfunc done() bool { return false }\n\nvar a, b int\nvar x bool\n\nfunc fn() {\n\tfor !done() {\n\n\t}\n\n\tfor done() {\n\n\t}\n\n\tfor a <= b && b <= a {\n\n\t}\n\n\tfor !x || !(a == b) {\n\n\t}\n\n\tfor !done() {\n\n\t\tprintln()\n\t}\n\n\tfor {\n\t\tprintln()\n\t\tif done() {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor {\n\t\tif done() {\n\t\t\tprintln()\n\t\t\tbreak\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "quickfix/qf1007/qf1007.go",
    "content": "package qf1007\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"QF1007\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Merge conditional assignment into variable declaration\",\n\t\tBefore: `\nx := false\nif someCondition {\n    x = true\n}`,\n\t\tAfter:    `x := someCondition`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityHint,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkConditionalAssignmentQ = pattern.MustParse(`(AssignStmt x@(Object _) \":=\" assign@(Builtin b@(Or \"true\" \"false\")))`)\nvar checkConditionalAssignmentIfQ = pattern.MustParse(`(IfStmt nil cond [(AssignStmt x@(Object _) \"=\" (Builtin b@(Or \"true\" \"false\")))] nil)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tvar body *ast.BlockStmt\n\t\tswitch node := node.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\tbody = node.Body\n\t\tcase *ast.FuncLit:\n\t\t\tbody = node.Body\n\t\tdefault:\n\t\t\tpanic(\"unreachable\")\n\t\t}\n\t\tif body == nil {\n\t\t\treturn\n\t\t}\n\n\t\tstmts := body.List\n\t\tif len(stmts) < 2 {\n\t\t\treturn\n\t\t}\n\t\tfor i, first := range stmts[:len(stmts)-1] {\n\t\t\tsecond := stmts[i+1]\n\t\t\tm1, ok := code.Match(pass, checkConditionalAssignmentQ, first)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tm2, ok := code.Match(pass, checkConditionalAssignmentIfQ, second)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif m1.State[\"x\"] != m2.State[\"x\"] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif m1.State[\"b\"] == m2.State[\"b\"] {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tv := m2.State[\"cond\"].(ast.Expr)\n\t\t\tif m1.State[\"b\"] == \"true\" {\n\t\t\t\tv = &ast.UnaryExpr{\n\t\t\t\t\tOp: token.NOT,\n\t\t\t\t\tX:  v,\n\t\t\t\t}\n\t\t\t}\n\t\t\treport.Report(pass, first, \"could merge conditional assignment into variable declaration\",\n\t\t\t\treport.Fixes(edit.Fix(\"Merge conditional assignment into variable declaration\",\n\t\t\t\t\tedit.ReplaceWithNode(pass.Fset, m1.State[\"assign\"].(ast.Node), v),\n\t\t\t\t\tedit.Delete(second))))\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "quickfix/qf1007/qf1007_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1007\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1007/testdata/go1.0/CheckConditionalAssignment/CheckConditionalAssignment.go",
    "content": "package pkg\n\nfunc foo() bool { return true }\n\nvar bar bool\nvar baz bool\n\nfunc fn() {\n\tx := false //@ diag(`merge conditional assignment`)\n\tif foo() || (bar && !baz) {\n\t\tx = true\n\t}\n\n\tx = false\n\tif foo() || (bar && !baz) {\n\t\tx = true\n\t}\n\n\ty := false\n\tif true {\n\t\ty = true\n\t\tprintln(y)\n\t}\n\n\tz := false\n\tif true {\n\t\tz = false\n\t}\n\n\ta := false\n\tif true {\n\t\tx = true\n\t}\n\n\tb := true //@ diag(`merge conditional assignment`)\n\tif foo() || (bar && !baz) {\n\t\tb = false\n\t}\n\n\tc := false\n\tif true {\n\t\tc = false\n\t}\n\n\td := true\n\tif true {\n\t\td = true\n\t}\n\n\t_ = x\n\t_ = y\n\t_ = z\n\t_ = a\n\t_ = b\n\t_ = c\n\t_ = d\n}\n"
  },
  {
    "path": "quickfix/qf1007/testdata/go1.0/CheckConditionalAssignment/CheckConditionalAssignment.go.golden",
    "content": "package pkg\n\nfunc foo() bool { return true }\n\nvar bar bool\nvar baz bool\n\nfunc fn() {\n\tx := foo() || (bar && !baz) //@ diag(`merge conditional assignment`)\n\n\tx = false\n\tif foo() || (bar && !baz) {\n\t\tx = true\n\t}\n\n\ty := false\n\tif true {\n\t\ty = true\n\t\tprintln(y)\n\t}\n\n\tz := false\n\tif true {\n\t\tz = false\n\t}\n\n\ta := false\n\tif true {\n\t\tx = true\n\t}\n\n\tb := !(foo() || (bar && !baz)) //@ diag(`merge conditional assignment`)\n\n\tc := false\n\tif true {\n\t\tc = false\n\t}\n\n\td := true\n\tif true {\n\t\td = true\n\t}\n\n\t_ = x\n\t_ = y\n\t_ = z\n\t_ = a\n\t_ = b\n\t_ = c\n\t_ = d\n}\n"
  },
  {
    "path": "quickfix/qf1008/qf1008.go",
    "content": "package qf1008\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"QF1008\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    \"Omit embedded fields from selector expression\",\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityHint,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\ttype Selector struct {\n\t\tNode   *ast.SelectorExpr\n\t\tX      ast.Expr\n\t\tFields []*ast.Ident\n\t}\n\n\t// extractSelectors extracts uninterrupted sequences of selector expressions.\n\t// For example, for a.b.c().d.e[0].f.g three sequences will be returned: (X=a, X.b.c), (X=a.b.c(), X.d.e), and (X=a.b.c().d.e[0], X.f.g)\n\t//\n\t// It returns nil if the provided selector expression is not the root of a set of sequences.\n\t// For example, for a.b.c, if node is b.c, no selectors will be returned.\n\textractSelectors := func(expr *ast.SelectorExpr) []Selector {\n\t\tpath, _ := astutil.PathEnclosingInterval(code.File(pass, expr), expr.Pos(), expr.Pos())\n\t\tfor i := len(path) - 1; i >= 0; i-- {\n\t\t\tif el, ok := path[i].(*ast.SelectorExpr); ok {\n\t\t\t\tif el != expr {\n\t\t\t\t\t// this expression is a subset of the entire chain, don't look at it.\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tinChain := false\n\t\tvar out []Selector\n\t\tfor _, el := range path {\n\t\t\tif expr, ok := el.(*ast.SelectorExpr); ok {\n\t\t\t\tif !inChain {\n\t\t\t\t\tinChain = true\n\t\t\t\t\tout = append(out, Selector{X: expr.X})\n\t\t\t\t}\n\t\t\t\tsel := &out[len(out)-1]\n\t\t\t\tsel.Fields = append(sel.Fields, expr.Sel)\n\t\t\t\tsel.Node = expr\n\t\t\t} else if inChain {\n\t\t\t\tinChain = false\n\t\t\t}\n\t\t}\n\t\treturn out\n\t}\n\n\tfn := func(node ast.Node) {\n\t\texpr := node.(*ast.SelectorExpr)\n\n\t\tif _, ok := expr.X.(*ast.SelectorExpr); !ok {\n\t\t\t// Avoid the expensive call to PathEnclosingInterval for the common 1-level deep selector, which cannot be shortened.\n\t\t\treturn\n\t\t}\n\n\t\tsels := extractSelectors(expr)\n\t\tif len(sels) == 0 {\n\t\t\treturn\n\t\t}\n\n\t\tfor _, sel := range sels {\n\t\tfieldLoop:\n\t\t\tfor base, fields := pass.TypesInfo.TypeOf(sel.X), sel.Fields; len(fields) >= 2; base, fields = pass.TypesInfo.ObjectOf(fields[0]).Type(), fields[1:] {\n\t\t\t\thop1 := fields[0]\n\t\t\t\thop2 := fields[1]\n\n\t\t\t\t// the selector expression might be a qualified identifier, which cannot be simplified\n\t\t\t\tif base == types.Typ[types.Invalid] {\n\t\t\t\t\tcontinue fieldLoop\n\t\t\t\t}\n\n\t\t\t\t// Check if we can skip a field in the chain of selectors.\n\t\t\t\t// We can skip a field 'b' if a.b.c and a.c resolve to the same object and take the same path.\n\t\t\t\t//\n\t\t\t\t// We set addressable to true unconditionally because we've already successfully type-checked the program,\n\t\t\t\t// which means either the selector doesn't need addressability, or it is addressable.\n\t\t\t\tleftObj, leftLeg, _ := types.LookupFieldOrMethod(base, true, pass.Pkg, hop1.Name)\n\n\t\t\t\t// We can't skip fields that aren't embedded\n\t\t\t\tif !leftObj.(*types.Var).Embedded() {\n\t\t\t\t\tcontinue fieldLoop\n\t\t\t\t}\n\n\t\t\t\tdirectObj, directPath, _ := types.LookupFieldOrMethod(base, true, pass.Pkg, hop2.Name)\n\n\t\t\t\t// Fail fast if omitting the embedded field leads to a different object\n\t\t\t\tif directObj != pass.TypesInfo.ObjectOf(hop2) {\n\t\t\t\t\tcontinue fieldLoop\n\t\t\t\t}\n\n\t\t\t\t_, rightLeg, _ := types.LookupFieldOrMethod(leftObj.Type(), true, pass.Pkg, hop2.Name)\n\n\t\t\t\t// Fail fast if the paths are obviously different\n\t\t\t\tif len(directPath) != len(leftLeg)+len(rightLeg) {\n\t\t\t\t\tcontinue fieldLoop\n\t\t\t\t}\n\n\t\t\t\t// Make sure that omitting the embedded field will take the same path to the final object.\n\t\t\t\t// Multiple paths involving different fields may lead to the same type-checker object, causing different runtime behavior.\n\t\t\t\tfor i := range directPath {\n\t\t\t\t\tif i < len(leftLeg) {\n\t\t\t\t\t\tif leftLeg[i] != directPath[i] {\n\t\t\t\t\t\t\tcontinue fieldLoop\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif rightLeg[i-len(leftLeg)] != directPath[i] {\n\t\t\t\t\t\t\tcontinue fieldLoop\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\te := edit.Delete(edit.Range{hop1.Pos(), hop2.Pos()})\n\t\t\t\treport.Report(pass, hop1, fmt.Sprintf(\"could remove embedded field %q from selector\", hop1.Name),\n\t\t\t\t\treport.Fixes(edit.Fix(fmt.Sprintf(\"Remove embedded field %q from selector\", hop1.Name), e)))\n\t\t\t}\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.SelectorExpr)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "quickfix/qf1008/qf1008_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1008\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-anon.go",
    "content": "package pkg\n\ntype AnonOuter struct{ AnonInner }\ntype AnonInner struct{ F4 struct{ F5 int } }\n\nfunc fnAnon() {\n\tvar anon AnonOuter\n\t_ = anon.AnonInner.F4.F5 //@ diag(`could remove embedded field \"AnonInner\" from selector`)\n\t_ = anon.F4.F5           // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-anon.go.golden",
    "content": "package pkg\n\ntype AnonOuter struct{ AnonInner }\ntype AnonInner struct{ F4 struct{ F5 int } }\n\nfunc fnAnon() {\n\tvar anon AnonOuter\n\t_ = anon.F4.F5 //@ diag(`could remove embedded field \"AnonInner\" from selector`)\n\t_ = anon.F4.F5 // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-basic.go",
    "content": "package pkg\n\ntype BasicOuter struct{ BasicInner }\ntype BasicInner struct{ F1 int }\n\nfunc fnBasic() {\n\tvar basic BasicOuter\n\t_ = basic.BasicInner.F1 //@ diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = basic.F1            // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-basic.go.golden",
    "content": "package pkg\n\ntype BasicOuter struct{ BasicInner }\ntype BasicInner struct{ F1 int }\n\nfunc fnBasic() {\n\tvar basic BasicOuter\n\t_ = basic.F1 //@ diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = basic.F1 // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-call.go",
    "content": "package pkg\n\ntype FunctionCallOuter struct{ FunctionCallInner }\ntype FunctionCallInner struct {\n\tF8 func() FunctionCallContinuedOuter\n}\ntype FunctionCallContinuedOuter struct{ FunctionCallContinuedInner }\ntype FunctionCallContinuedInner struct{ F9 int }\n\nfunc fnCall() {\n\tvar call FunctionCallOuter\n\t_ = call.FunctionCallInner.F8().FunctionCallContinuedInner.F9 //@ diag(`could remove embedded field \"FunctionCallInner\" from selector`), diag(`could remove embedded field \"FunctionCallContinuedInner\" from selector`)\n\t_ = call.F8().F9                                              // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-call.go.golden",
    "content": "-- Remove embedded field \"FunctionCallInner\" from selector --\npackage pkg\n\ntype FunctionCallOuter struct{ FunctionCallInner }\ntype FunctionCallInner struct {\n\tF8 func() FunctionCallContinuedOuter\n}\ntype FunctionCallContinuedOuter struct{ FunctionCallContinuedInner }\ntype FunctionCallContinuedInner struct{ F9 int }\n\nfunc fnCall() {\n\tvar call FunctionCallOuter\n\t_ = call.F8().FunctionCallContinuedInner.F9 //@ diag(`could remove embedded field \"FunctionCallInner\" from selector`), diag(`could remove embedded field \"FunctionCallContinuedInner\" from selector`)\n\t_ = call.F8().F9                            // minimal form\n}\n\n-- Remove embedded field \"FunctionCallContinuedInner\" from selector --\npackage pkg\n\ntype FunctionCallOuter struct{ FunctionCallInner }\ntype FunctionCallInner struct {\n\tF8 func() FunctionCallContinuedOuter\n}\ntype FunctionCallContinuedOuter struct{ FunctionCallContinuedInner }\ntype FunctionCallContinuedInner struct{ F9 int }\n\nfunc fnCall() {\n\tvar call FunctionCallOuter\n\t_ = call.FunctionCallInner.F8().F9 //@ diag(`could remove embedded field \"FunctionCallInner\" from selector`), diag(`could remove embedded field \"FunctionCallContinuedInner\" from selector`)\n\t_ = call.F8().F9                   // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-depth.go",
    "content": "package pkg\n\nfunc fnDepth() {\n\ttype T4 struct{ F int }\n\ttype T5 struct{ T4 }\n\ttype T3 struct{ T5 }\n\ttype T2 struct{ T4 }\n\n\ttype T1 struct {\n\t\tT2\n\t\tT3\n\t}\n\n\tvar v T1\n\t_ = v.F\n\t_ = v.T2.F //@ diag(`could remove embedded field \"T2\" from selector`)\n\t_ = v.T3.F\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-depth.go.golden",
    "content": "package pkg\n\nfunc fnDepth() {\n\ttype T4 struct{ F int }\n\ttype T5 struct{ T4 }\n\ttype T3 struct{ T5 }\n\ttype T2 struct{ T4 }\n\n\ttype T1 struct {\n\t\tT2\n\t\tT3\n\t}\n\n\tvar v T1\n\t_ = v.F\n\t_ = v.F //@ diag(`could remove embedded field \"T2\" from selector`)\n\t_ = v.T3.F\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-multi.go",
    "content": "package pkg\n\ntype MultiLevel struct{ BasicOuter }\n\nfunc fnMulti() {\n\tvar multi MultiLevel\n\t_ = multi.BasicOuter.BasicInner.F1 //@ diag(`could remove embedded field \"BasicOuter\" from selector`), diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = multi.BasicOuter.F1            //@ diag(`could remove embedded field \"BasicOuter\" from selector`)\n\t_ = multi.BasicInner.F1            //@ diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = multi.F1                       // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-multi.go.golden",
    "content": "-- Remove embedded field \"BasicInner\" from selector --\npackage pkg\n\ntype MultiLevel struct{ BasicOuter }\n\nfunc fnMulti() {\n\tvar multi MultiLevel\n\t_ = multi.BasicOuter.F1 //@ diag(`could remove embedded field \"BasicOuter\" from selector`), diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = multi.BasicOuter.F1 //@ diag(`could remove embedded field \"BasicOuter\" from selector`)\n\t_ = multi.F1            //@ diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = multi.F1            // minimal form\n}\n\n-- Remove embedded field \"BasicOuter\" from selector --\npackage pkg\n\ntype MultiLevel struct{ BasicOuter }\n\nfunc fnMulti() {\n\tvar multi MultiLevel\n\t_ = multi.BasicInner.F1 //@ diag(`could remove embedded field \"BasicOuter\" from selector`), diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = multi.F1            //@ diag(`could remove embedded field \"BasicOuter\" from selector`)\n\t_ = multi.BasicInner.F1 //@ diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = multi.F1            // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-multi2.go",
    "content": "package pkg\n\ntype Embedded2 struct{}\ntype Embedded struct{ Embedded2 }\ntype Wrapper struct{ Embedded }\n\nfunc (e Embedded2) String() string { return \"\" }\nfunc (w Wrapper) String() string {\n\t// Either Embedded or Embedded2 can be removed, but removing both would\n\t// change the semantics\n\treturn w.Embedded.Embedded2.String() //@ diag(`could remove embedded field \"Embedded\"`), diag(`could remove embedded field \"Embedded2`)\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-multi2.go.golden",
    "content": "-- Remove embedded field \"Embedded\" from selector --\npackage pkg\n\ntype Embedded2 struct{}\ntype Embedded struct{ Embedded2 }\ntype Wrapper struct{ Embedded }\n\nfunc (e Embedded2) String() string { return \"\" }\nfunc (w Wrapper) String() string {\n\t// Either Embedded or Embedded2 can be removed, but removing both would\n\t// change the semantics\n\treturn w.Embedded2.String() //@ diag(`could remove embedded field \"Embedded\"`), diag(`could remove embedded field \"Embedded2`)\n}\n\n-- Remove embedded field \"Embedded2\" from selector --\npackage pkg\n\ntype Embedded2 struct{}\ntype Embedded struct{ Embedded2 }\ntype Wrapper struct{ Embedded }\n\nfunc (e Embedded2) String() string { return \"\" }\nfunc (w Wrapper) String() string {\n\t// Either Embedded or Embedded2 can be removed, but removing both would\n\t// change the semantics\n\treturn w.Embedded.String() //@ diag(`could remove embedded field \"Embedded\"`), diag(`could remove embedded field \"Embedded2`)\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-partial-multi.go",
    "content": "package pkg\n\ntype PartialMultiLevel struct{ F3 MultiLevel }\ntype PartialMultiLevel2Outer struct{ PartialMultiLevel2Inner }\ntype PartialMultiLevel2Inner struct{ F6 PartialMultiLevel2Outer2 }\ntype PartialMultiLevel2Outer2 struct{ PartialMultiLevel2Inner2 }\ntype PartialMultiLevel2Inner2 struct{ F7 int }\n\nfunc fnPartialMulti() {\n\tvar partialMulti PartialMultiLevel\n\t_ = partialMulti.F3.BasicOuter.F1            //@ diag(`could remove embedded field \"BasicOuter\" from selector`)\n\t_ = partialMulti.F3.BasicOuter.BasicInner.F1 //@ diag(`could remove embedded field \"BasicOuter\" from selector`), diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = partialMulti.F3.F1                       // minimal form\n\n\tvar partialMulti2 PartialMultiLevel2Outer\n\t_ = partialMulti2.PartialMultiLevel2Inner.F6.PartialMultiLevel2Inner2.F7 //@ diag(`could remove embedded field \"PartialMultiLevel2Inner2\" from selector`), diag(`could remove embedded field \"PartialMultiLevel2Inner\" from selector`)\n\t_ = partialMulti2.F6.F7                                                  // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-partial-multi.go.golden",
    "content": "-- Remove embedded field \"BasicOuter\" from selector --\npackage pkg\n\ntype PartialMultiLevel struct{ F3 MultiLevel }\ntype PartialMultiLevel2Outer struct{ PartialMultiLevel2Inner }\ntype PartialMultiLevel2Inner struct{ F6 PartialMultiLevel2Outer2 }\ntype PartialMultiLevel2Outer2 struct{ PartialMultiLevel2Inner2 }\ntype PartialMultiLevel2Inner2 struct{ F7 int }\n\nfunc fnPartialMulti() {\n\tvar partialMulti PartialMultiLevel\n\t_ = partialMulti.F3.F1            //@ diag(`could remove embedded field \"BasicOuter\" from selector`)\n\t_ = partialMulti.F3.BasicInner.F1 //@ diag(`could remove embedded field \"BasicOuter\" from selector`), diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = partialMulti.F3.F1            // minimal form\n\n\tvar partialMulti2 PartialMultiLevel2Outer\n\t_ = partialMulti2.PartialMultiLevel2Inner.F6.PartialMultiLevel2Inner2.F7 //@ diag(`could remove embedded field \"PartialMultiLevel2Inner2\" from selector`), diag(`could remove embedded field \"PartialMultiLevel2Inner\" from selector`)\n\t_ = partialMulti2.F6.F7                                                  // minimal form\n}\n\n-- Remove embedded field \"BasicInner\" from selector --\npackage pkg\n\ntype PartialMultiLevel struct{ F3 MultiLevel }\ntype PartialMultiLevel2Outer struct{ PartialMultiLevel2Inner }\ntype PartialMultiLevel2Inner struct{ F6 PartialMultiLevel2Outer2 }\ntype PartialMultiLevel2Outer2 struct{ PartialMultiLevel2Inner2 }\ntype PartialMultiLevel2Inner2 struct{ F7 int }\n\nfunc fnPartialMulti() {\n\tvar partialMulti PartialMultiLevel\n\t_ = partialMulti.F3.BasicOuter.F1 //@ diag(`could remove embedded field \"BasicOuter\" from selector`)\n\t_ = partialMulti.F3.BasicOuter.F1 //@ diag(`could remove embedded field \"BasicOuter\" from selector`), diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = partialMulti.F3.F1            // minimal form\n\n\tvar partialMulti2 PartialMultiLevel2Outer\n\t_ = partialMulti2.PartialMultiLevel2Inner.F6.PartialMultiLevel2Inner2.F7 //@ diag(`could remove embedded field \"PartialMultiLevel2Inner2\" from selector`), diag(`could remove embedded field \"PartialMultiLevel2Inner\" from selector`)\n\t_ = partialMulti2.F6.F7                                                  // minimal form\n}\n\n-- Remove embedded field \"PartialMultiLevel2Inner2\" from selector --\npackage pkg\n\ntype PartialMultiLevel struct{ F3 MultiLevel }\ntype PartialMultiLevel2Outer struct{ PartialMultiLevel2Inner }\ntype PartialMultiLevel2Inner struct{ F6 PartialMultiLevel2Outer2 }\ntype PartialMultiLevel2Outer2 struct{ PartialMultiLevel2Inner2 }\ntype PartialMultiLevel2Inner2 struct{ F7 int }\n\nfunc fnPartialMulti() {\n\tvar partialMulti PartialMultiLevel\n\t_ = partialMulti.F3.BasicOuter.F1            //@ diag(`could remove embedded field \"BasicOuter\" from selector`)\n\t_ = partialMulti.F3.BasicOuter.BasicInner.F1 //@ diag(`could remove embedded field \"BasicOuter\" from selector`), diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = partialMulti.F3.F1                       // minimal form\n\n\tvar partialMulti2 PartialMultiLevel2Outer\n\t_ = partialMulti2.PartialMultiLevel2Inner.F6.F7 //@ diag(`could remove embedded field \"PartialMultiLevel2Inner2\" from selector`), diag(`could remove embedded field \"PartialMultiLevel2Inner\" from selector`)\n\t_ = partialMulti2.F6.F7                         // minimal form\n}\n\n-- Remove embedded field \"PartialMultiLevel2Inner\" from selector --\npackage pkg\n\ntype PartialMultiLevel struct{ F3 MultiLevel }\ntype PartialMultiLevel2Outer struct{ PartialMultiLevel2Inner }\ntype PartialMultiLevel2Inner struct{ F6 PartialMultiLevel2Outer2 }\ntype PartialMultiLevel2Outer2 struct{ PartialMultiLevel2Inner2 }\ntype PartialMultiLevel2Inner2 struct{ F7 int }\n\nfunc fnPartialMulti() {\n\tvar partialMulti PartialMultiLevel\n\t_ = partialMulti.F3.BasicOuter.F1            //@ diag(`could remove embedded field \"BasicOuter\" from selector`)\n\t_ = partialMulti.F3.BasicOuter.BasicInner.F1 //@ diag(`could remove embedded field \"BasicOuter\" from selector`), diag(`could remove embedded field \"BasicInner\" from selector`)\n\t_ = partialMulti.F3.F1                       // minimal form\n\n\tvar partialMulti2 PartialMultiLevel2Outer\n\t_ = partialMulti2.F6.PartialMultiLevel2Inner2.F7 //@ diag(`could remove embedded field \"PartialMultiLevel2Inner2\" from selector`), diag(`could remove embedded field \"PartialMultiLevel2Inner\" from selector`)\n\t_ = partialMulti2.F6.F7                          // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-qualified.go",
    "content": "package pkg\n\nimport (\n\t\"io\"\n\n\tassist \"example.com/CheckExplicitEmbeddedSelectorassist\"\n)\n\nfunc fnQualified() {\n\t_ = io.EOF.Error  // minimal form\n\t_ = assist.V.T2.F //@ diag(`could remove embedded field \"T2\" from selector`)\n\t_ = assist.V.F    // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-qualified.go.golden",
    "content": "package pkg\n\nimport (\n\t\"io\"\n\n\tassist \"example.com/CheckExplicitEmbeddedSelectorassist\"\n)\n\nfunc fnQualified() {\n\t_ = io.EOF.Error // minimal form\n\t_ = assist.V.F   //@ diag(`could remove embedded field \"T2\" from selector`)\n\t_ = assist.V.F   // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-recursive.go",
    "content": "package pkg\n\ntype T1 struct {\n\tNext *T1\n}\n\ntype T2 struct {\n\tF int\n\t*T2\n\tT3\n}\n\ntype T3 struct {\n\tF2 int\n}\n\nfunc (*T1) Foo() {}\nfunc (*T2) Foo() {}\n\nfunc fn() {\n\tvar t1 T1\n\tvar t2 T2\n\t_ = t1.Next.Foo\n\t_ = t2.T2.Foo\n\t_ = t2.T2.F\n\t_ = t2.T3.F2 //@ diag(`could remove embedded field \"T3\" from selector`)\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-recursive.go.golden",
    "content": "package pkg\n\ntype T1 struct {\n\tNext *T1\n}\n\ntype T2 struct {\n\tF int\n\t*T2\n\tT3\n}\n\ntype T3 struct {\n\tF2 int\n}\n\nfunc (*T1) Foo() {}\nfunc (*T2) Foo() {}\n\nfunc fn() {\n\tvar t1 T1\n\tvar t2 T2\n\t_ = t1.Next.Foo\n\t_ = t2.T2.Foo\n\t_ = t2.T2.F\n\t_ = t2.F2 //@ diag(`could remove embedded field \"T3\" from selector`)\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-shadowing.go",
    "content": "package pkg\n\ntype Shadowing struct {\n\tF1 int\n\tBasicInner\n}\n\nfunc fnShadowing() {\n\tvar shadowing Shadowing\n\t_ = shadowing.BasicInner.F1 // can't be simplified due to shadowing\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-unexported.go",
    "content": "package pkg\n\ntype UnexportedSamePackageOuter struct {\n\tunexportedSamePackageInner\n}\n\ntype unexportedSamePackageInner struct {\n\tF10 int\n}\n\nfunc fnUnexported() {\n\tvar unexportedSame UnexportedSamePackageOuter\n\t_ = unexportedSame.unexportedSamePackageInner.F10 //@ diag(`could remove embedded field \"unexportedSamePackageInner\" from selector`)\n\t_ = unexportedSame.F10                            // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelector/CheckExplicitEmbeddedSelector-unexported.go.golden",
    "content": "package pkg\n\ntype UnexportedSamePackageOuter struct {\n\tunexportedSamePackageInner\n}\n\ntype unexportedSamePackageInner struct {\n\tF10 int\n}\n\nfunc fnUnexported() {\n\tvar unexportedSame UnexportedSamePackageOuter\n\t_ = unexportedSame.F10 //@ diag(`could remove embedded field \"unexportedSamePackageInner\" from selector`)\n\t_ = unexportedSame.F10 // minimal form\n}\n"
  },
  {
    "path": "quickfix/qf1008/testdata/go1.0/CheckExplicitEmbeddedSelectorassist/assist.go",
    "content": "package assist\n\ntype T1 struct {\n\tT2\n}\n\ntype T2 struct {\n\tF int\n}\n\nvar V T1\n"
  },
  {
    "path": "quickfix/qf1009/qf1009.go",
    "content": "package qf1009\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"QF1009\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Use \\'time.Time.Equal\\' instead of \\'==\\' operator`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityInfo,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar timeEqualR = pattern.MustParse(`(CallExpr (SelectorExpr lhs (Ident \"Equal\")) rhs)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// FIXME(dh): create proper suggested fix for renamed import\n\n\tfn := func(node ast.Node) {\n\t\texpr := node.(*ast.BinaryExpr)\n\t\tif expr.Op != token.EQL {\n\t\t\treturn\n\t\t}\n\t\tif !code.IsOfTypeWithName(pass, expr.X, \"time.Time\") || !code.IsOfTypeWithName(pass, expr.Y, \"time.Time\") {\n\t\t\treturn\n\t\t}\n\t\treport.Report(pass, node, \"probably want to use time.Time.Equal instead\",\n\t\t\treport.Fixes(edit.Fix(\"Use time.Time.Equal method\",\n\t\t\t\tedit.ReplaceWithPattern(pass.Fset, node, timeEqualR, pattern.State{\"lhs\": expr.X, \"rhs\": expr.Y}))))\n\t}\n\tcode.Preorder(pass, fn, (*ast.BinaryExpr)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "quickfix/qf1009/qf1009_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1009\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1009/testdata/go1.0/CheckTimeEquality/CheckTimeEquality.go",
    "content": "package pkg\n\nimport \"time\"\n\nfunc foo() time.Time { return time.Time{} }\nfunc bar() time.Time { return time.Time{} }\n\nfunc fn() {\n\tvar t1, t2 time.Time\n\tif t1 == t2 { //@ diag(`probably want to use time.Time.Equal instead`)\n\t}\n\n\tif foo() == bar() { //@ diag(`probably want to use time.Time.Equal instead`)\n\t}\n}\n"
  },
  {
    "path": "quickfix/qf1009/testdata/go1.0/CheckTimeEquality/CheckTimeEquality.go.golden",
    "content": "package pkg\n\nimport \"time\"\n\nfunc foo() time.Time { return time.Time{} }\nfunc bar() time.Time { return time.Time{} }\n\nfunc fn() {\n\tvar t1, t2 time.Time\n\tif t1.Equal(t2) { //@ diag(`probably want to use time.Time.Equal instead`)\n\t}\n\n\tif foo().Equal(bar()) { //@ diag(`probably want to use time.Time.Equal instead`)\n\t}\n}\n"
  },
  {
    "path": "quickfix/qf1010/qf1010.go",
    "content": "package qf1010\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/knowledge\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"QF1010\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    \"Convert slice of bytes to string when printing it\",\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityHint,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar byteSlicePrintingQ = pattern.MustParse(`\n\t(Or\n\t\t(CallExpr\n\t\t\t(Symbol (Or\n\t\t\t\t\"fmt.Print\"\n\t\t\t\t\"fmt.Println\"\n\t\t\t\t\"fmt.Sprint\"\n\t\t\t\t\"fmt.Sprintln\"\n\t\t\t\t\"log.Fatal\"\n\t\t\t\t\"log.Fatalln\"\n\t\t\t\t\"log.Panic\"\n\t\t\t\t\"log.Panicln\"\n\t\t\t\t\"log.Print\"\n\t\t\t\t\"log.Println\"\n\t\t\t\t\"(*log.Logger).Fatal\"\n\t\t\t\t\"(*log.Logger).Fatalln\"\n\t\t\t\t\"(*log.Logger).Panic\"\n\t\t\t\t\"(*log.Logger).Panicln\"\n\t\t\t\t\"(*log.Logger).Print\"\n\t\t\t\t\"(*log.Logger).Println\")) args)\n\n\t\t(CallExpr (Symbol (Or\n\t\t\t\"fmt.Fprint\"\n\t\t\t\"fmt.Fprintln\")) _:args))`)\n\nvar byteSlicePrintingR = pattern.MustParse(`(CallExpr (Ident \"string\") [arg])`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, m := range code.Matches(pass, byteSlicePrintingQ) {\n\t\targs := m.State[\"args\"].([]ast.Expr)\n\t\tfor _, arg := range args {\n\t\t\tif !code.IsOfStringConvertibleByteSlice(pass, arg) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif types.Implements(pass.TypesInfo.TypeOf(arg), knowledge.Interfaces[\"fmt.Stringer\"]) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfix := edit.Fix(\"Convert argument to string\", edit.ReplaceWithPattern(pass.Fset, arg, byteSlicePrintingR, pattern.State{\"arg\": arg}))\n\t\t\treport.Report(pass, arg, \"could convert argument to string\", report.Fixes(fix))\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "quickfix/qf1010/qf1010_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1010\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1010/testdata/go1.0/CheckByteSlicePrinting/CheckByteSlicePrinting.go",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype Stringable []byte\n\nfunc (*Stringable) String() string { return \"\" }\n\nfunc fn() {\n\ttype ByteSlice []byte\n\tvar b1 []byte\n\tvar b2 ByteSlice\n\tvar b3 *Stringable\n\tvar b4 Stringable\n\n\tvar s string\n\tfmt.Print(1, b1, 2, []byte(\"\"), b2, s)       //@ diag(`could convert argument to string`), diag(`could convert argument to string`), diag(`could convert argument to string`)\n\tfmt.Fprint(nil, 1, b1, 2, []byte(\"\"), b2, s) //@ diag(`could convert argument to string`), diag(`could convert argument to string`), diag(`could convert argument to string`)\n\tfmt.Print()\n\tfmt.Fprint(nil)\n\n\tfmt.Println(b3)\n\tfmt.Println(b4) //@ diag(`could convert argument to string`)\n}\n"
  },
  {
    "path": "quickfix/qf1010/testdata/go1.0/CheckByteSlicePrinting/CheckByteSlicePrinting.go.golden",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype Stringable []byte\n\nfunc (*Stringable) String() string { return \"\" }\n\nfunc fn() {\n\ttype ByteSlice []byte\n\tvar b1 []byte\n\tvar b2 ByteSlice\n\tvar b3 *Stringable\n\tvar b4 Stringable\n\n\tvar s string\n\tfmt.Print(1, string(b1), 2, string([]byte(\"\")), string(b2), s)       //@ diag(`could convert argument to string`), diag(`could convert argument to string`), diag(`could convert argument to string`)\n\tfmt.Fprint(nil, 1, string(b1), 2, string([]byte(\"\")), string(b2), s) //@ diag(`could convert argument to string`), diag(`could convert argument to string`), diag(`could convert argument to string`)\n\tfmt.Print()\n\tfmt.Fprint(nil)\n\n\tfmt.Println(b3)\n\tfmt.Println(string(b4)) //@ diag(`could convert argument to string`)\n}\n"
  },
  {
    "path": "quickfix/qf1010/testdata/go1.9/CheckByteSlicePrinting/CheckByteSlicePrinting.go",
    "content": "package pkg\n\nimport \"fmt\"\n\nfunc fn() {\n\ttype ByteSlice []byte\n\ttype Alias1 = []byte\n\ttype Alias2 = ByteSlice\n\ttype Alias3 = byte\n\ttype Alias4 = []Alias3\n\n\tfmt.Print(Alias1{}) //@ diag(`could convert argument to string`)\n\tfmt.Print(Alias2{}) //@ diag(`could convert argument to string`)\n\tfmt.Print(Alias4{}) //@ diag(`could convert argument to string`)\n\tfmt.Print()\n\tfmt.Fprint(nil)\n}\n"
  },
  {
    "path": "quickfix/qf1010/testdata/go1.9/CheckByteSlicePrinting/CheckByteSlicePrinting.go.golden",
    "content": "package pkg\n\nimport \"fmt\"\n\nfunc fn() {\n\ttype ByteSlice []byte\n\ttype Alias1 = []byte\n\ttype Alias2 = ByteSlice\n\ttype Alias3 = byte\n\ttype Alias4 = []Alias3\n\n\tfmt.Print(string(Alias1{})) //@ diag(`could convert argument to string`)\n\tfmt.Print(string(Alias2{})) //@ diag(`could convert argument to string`)\n\tfmt.Print(string(Alias4{})) //@ diag(`could convert argument to string`)\n\tfmt.Print()\n\tfmt.Fprint(nil)\n}\n"
  },
  {
    "path": "quickfix/qf1011/qf1011.go",
    "content": "package qf1011\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/sharedcheck\"\n)\n\nfunc init() {\n\tSCAnalyzer.Analyzer.Name = \"QF1011\"\n}\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: sharedcheck.RedundantTypeInDeclarationChecker(\"could\", true),\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    \"Omit redundant type from variable declaration\",\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityHint,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n"
  },
  {
    "path": "quickfix/qf1011/qf1011_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1011\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1011/testdata/go1.0/CheckRedundantTypeInDeclaration/CheckRedundantTypeInDeclaration.go",
    "content": "package pkg\n\nimport (\n\t\"io\"\n\t\"math\"\n)\n\ntype MyInt int\n\nconst X int = 1\nconst Y = 1\n\nfunc gen1() int           { return 0 }\nfunc gen2() io.ReadCloser { return nil }\nfunc gen3() MyInt         { return 0 }\n\n// don't flag global variables\nvar a int = gen1()\n\nfunc fn() {\n\tvar _ int = gen1()           //@ diag(`could omit type int`)\n\tvar a int = Y                //@ diag(`could omit type int`)\n\tvar b int = 1                //@ diag(`could omit type int`)\n\tvar c int = 1.0              // different default type\n\tvar d MyInt = 1              // different default type\n\tvar e io.ReadCloser = gen2() //@ diag(`could omit type io.ReadCloser`)\n\tvar f io.Reader = gen2()     // different interface type\n\tvar g float64 = math.Pi      //@ diag(`could omit type float64`)\n\tvar h bool = true            //@ diag(`could omit type bool`)\n\tvar i string = \"\"            //@ diag(`could omit type string`)\n\tvar j MyInt = gen3()         //@ diag(`could omit type MyInt`)\n\tvar k uint8 = Y              // different default type on constant\n\tvar l uint8 = (Y + Y) / 2    // different default type on rhs\n\tvar m int = (Y + Y) / 2      //@ diag(`could omit type int`)\n\n\t_, _, _, _, _, _, _, _, _, _, _, _, _ = a, b, c, d, e, f, g, h, i, j, k, l, m\n}\n"
  },
  {
    "path": "quickfix/qf1011/testdata/go1.0/CheckRedundantTypeInDeclaration/CheckRedundantTypeInDeclaration.go.golden",
    "content": "package pkg\n\nimport (\n\t\"io\"\n\t\"math\"\n)\n\ntype MyInt int\n\nconst X int = 1\nconst Y = 1\n\nfunc gen1() int           { return 0 }\nfunc gen2() io.ReadCloser { return nil }\nfunc gen3() MyInt         { return 0 }\n\n// don't flag global variables\nvar a int = gen1()\n\nfunc fn() {\n\tvar _ = gen1()            //@ diag(`could omit type int`)\n\tvar a = Y                 //@ diag(`could omit type int`)\n\tvar b = 1                 //@ diag(`could omit type int`)\n\tvar c int = 1.0           // different default type\n\tvar d MyInt = 1           // different default type\n\tvar e = gen2()            //@ diag(`could omit type io.ReadCloser`)\n\tvar f io.Reader = gen2()  // different interface type\n\tvar g = math.Pi           //@ diag(`could omit type float64`)\n\tvar h = true              //@ diag(`could omit type bool`)\n\tvar i = \"\"                //@ diag(`could omit type string`)\n\tvar j = gen3()            //@ diag(`could omit type MyInt`)\n\tvar k uint8 = Y           // different default type on constant\n\tvar l uint8 = (Y + Y) / 2 // different default type on rhs\n\tvar m = (Y + Y) / 2       //@ diag(`could omit type int`)\n\n\t_, _, _, _, _, _, _, _, _, _, _, _, _ = a, b, c, d, e, f, g, h, i, j, k, l, m\n}\n"
  },
  {
    "path": "quickfix/qf1011/testdata/go1.9/CheckRedundantTypeInDeclaration/README",
    "content": "The cgo test case needs Go 1.9 because modern cgo generates code that\nuses aliases."
  },
  {
    "path": "quickfix/qf1011/testdata/go1.9/CheckRedundantTypeInDeclaration/cgo.go",
    "content": "package pkg\n\n// #include <stdlib.h>\nimport \"C\"\nimport \"unsafe\"\n\nfunc fnCgo(arg C.size_t) {\n\tvar ptr unsafe.Pointer\n\tC.realloc(ptr, arg)\n}\n"
  },
  {
    "path": "quickfix/qf1011/testdata/go1.9/CheckRedundantTypeInDeclaration/cgo.golden",
    "content": "package pkg\n\n// #include <stdlib.h>\nimport \"C\"\nimport \"unsafe\"\n\nfunc fnCgo(arg C.size_t) {\n\tvar ptr unsafe.Pointer\n\tC.realloc(ptr, arg)\n}\n"
  },
  {
    "path": "quickfix/qf1012/qf1012.go",
    "content": "package qf1012\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/knowledge\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"QF1012\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Use \\'fmt.Fprintf(x, ...)\\' instead of \\'x.Write(fmt.Sprintf(...))\\'`,\n\t\tSince:    \"2022.1\",\n\t\tSeverity: lint.SeverityHint,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckWriteBytesSprintfQ = pattern.MustParse(`\n\t(CallExpr\n\t\t(SelectorExpr recv (Ident \"Write\"))\n\t\t(CallExpr (ArrayType nil (Ident \"byte\"))\n\t\t\t(CallExpr\n\t\t\t\tfn@(Or\n\t\t\t\t\t(Symbol \"fmt.Sprint\")\n\t\t\t\t\t(Symbol \"fmt.Sprintf\")\n\t\t\t\t\t(Symbol \"fmt.Sprintln\"))\n\t\t\t\targs)\n\t))`)\n\n\tcheckWriteStringSprintfQ = pattern.MustParse(`\n\t(CallExpr\n\t\t(SelectorExpr recv (Ident \"WriteString\"))\n\t\t(CallExpr\n\t\t\tfn@(Or\n\t\t\t\t(Symbol \"fmt.Sprint\")\n\t\t\t\t(Symbol \"fmt.Sprintf\")\n\t\t\t\t(Symbol \"fmt.Sprintln\"))\n\t\t\targs))`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tgetRecv := func(m *pattern.Matcher) (ast.Expr, types.Type) {\n\t\t\trecv := m.State[\"recv\"].(ast.Expr)\n\t\t\trecvT := pass.TypesInfo.TypeOf(recv)\n\n\t\t\t// Use *N, not N, for the interface check if N\n\t\t\t// is a named non-interface type, since the pointer\n\t\t\t// has a larger method set (https://staticcheck.dev/issues/1097).\n\t\t\t// We assume the receiver expression is addressable\n\t\t\t// since otherwise the code wouldn't compile.\n\t\t\tif _, ok := types.Unalias(recvT).(*types.Named); ok && !types.IsInterface(recvT) {\n\t\t\t\trecvT = types.NewPointer(recvT)\n\t\t\t\trecv = &ast.UnaryExpr{Op: token.AND, X: recv}\n\n\t\t\t}\n\t\t\treturn recv, recvT\n\t\t}\n\n\t\tif m, ok := code.Match(pass, checkWriteBytesSprintfQ, node); ok {\n\t\t\trecv, recvT := getRecv(m)\n\t\t\tif !types.Implements(recvT, knowledge.Interfaces[\"io.Writer\"]) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tname := m.State[\"fn\"].(*types.Func).Name()\n\t\t\tnewName := \"F\" + strings.TrimPrefix(name, \"S\")\n\t\t\tmsg := fmt.Sprintf(\"Use fmt.%s(...) instead of Write([]byte(fmt.%s(...)))\", newName, name)\n\n\t\t\targs := m.State[\"args\"].([]ast.Expr)\n\t\t\tfix := edit.Fix(msg, edit.ReplaceWithNode(pass.Fset, node, &ast.CallExpr{\n\t\t\t\tFun: &ast.SelectorExpr{\n\t\t\t\t\tX:   ast.NewIdent(\"fmt\"),\n\t\t\t\t\tSel: ast.NewIdent(newName),\n\t\t\t\t},\n\t\t\t\tArgs: append([]ast.Expr{recv}, args...),\n\t\t\t}))\n\t\t\treport.Report(pass, node, msg, report.Fixes(fix))\n\t\t} else if m, ok := code.Match(pass, checkWriteStringSprintfQ, node); ok {\n\t\t\trecv, recvT := getRecv(m)\n\t\t\tif !types.Implements(recvT, knowledge.Interfaces[\"io.StringWriter\"]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// The type needs to implement both StringWriter and Writer.\n\t\t\t// If it doesn't implement Writer, then we cannot pass it to fmt.Fprint.\n\t\t\tif !types.Implements(recvT, knowledge.Interfaces[\"io.Writer\"]) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tname := m.State[\"fn\"].(*types.Func).Name()\n\t\t\tnewName := \"F\" + strings.TrimPrefix(name, \"S\")\n\t\t\tmsg := fmt.Sprintf(\"Use fmt.%s(...) instead of WriteString(fmt.%s(...))\", newName, name)\n\n\t\t\targs := m.State[\"args\"].([]ast.Expr)\n\t\t\tfix := edit.Fix(msg, edit.ReplaceWithNode(pass.Fset, node, &ast.CallExpr{\n\t\t\t\tFun: &ast.SelectorExpr{\n\t\t\t\t\tX:   ast.NewIdent(\"fmt\"),\n\t\t\t\t\tSel: ast.NewIdent(newName),\n\t\t\t\t},\n\t\t\t\tArgs: append([]ast.Expr{recv}, args...),\n\t\t\t}))\n\t\t\treport.Report(pass, node, msg, report.Fixes(fix))\n\t\t}\n\t}\n\tif !code.CouldMatchAny(pass, checkWriteBytesSprintfQ, checkWriteStringSprintfQ) {\n\t\treturn nil, nil\n\t}\n\tcode.Preorder(pass, fn, (*ast.CallExpr)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "quickfix/qf1012/qf1012_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage qf1012\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "quickfix/qf1012/testdata/go1.0/CheckWriteBytesSprintf/CheckWriteBytesSprintf.go",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n)\n\ntype NotAWriter struct{}\n\nfunc (NotAWriter) Write(b []byte) {}\n\nfunc fn1() {\n\tvar w io.Writer\n\tvar w2 NotAWriter\n\n\tw.Write([]byte(fmt.Sprint(\"abc\", \"de\")))   //@ diag(`Use fmt.Fprint`)\n\tw.Write([]byte(fmt.Sprintf(\"%T\", w)))      //@ diag(`Use fmt.Fprintf`)\n\tw.Write([]byte(fmt.Sprintln(\"abc\", \"de\"))) //@ diag(`Use fmt.Fprintln`)\n\n\tw2.Write([]byte(fmt.Sprint(\"abc\", \"de\")))\n}\n\nfunc fn2() {\n\tbuf := new(bytes.Buffer)\n\tvar sw io.StringWriter\n\n\tbuf.WriteString(fmt.Sprint(\"abc\", \"de\"))   //@ diag(`Use fmt.Fprint`)\n\tbuf.WriteString(fmt.Sprintf(\"%T\", 0))      //@ diag(`Use fmt.Fprintf`)\n\tbuf.WriteString(fmt.Sprintln(\"abc\", \"de\")) //@ diag(`Use fmt.Fprintln`)\n\n\t// We can't suggest fmt.Fprint here. We don't know if sw implements io.Writer.\n\tsw.WriteString(fmt.Sprint(\"abc\", \"de\"))\n\tsw.WriteString(fmt.Sprintf(\"%T\", 0))\n\tsw.WriteString(fmt.Sprintln(\"abc\", \"de\"))\n}\n\nfunc fn3() {\n\tvar buf bytes.Buffer\n\tbuf.WriteString(fmt.Sprint(\"abc\", \"de\")) //@ diag(`Use fmt.Fprint`)\n}\n"
  },
  {
    "path": "quickfix/qf1012/testdata/go1.0/CheckWriteBytesSprintf/CheckWriteBytesSprintf.go.golden",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n)\n\ntype NotAWriter struct{}\n\nfunc (NotAWriter) Write(b []byte) {}\n\nfunc fn1() {\n\tvar w io.Writer\n\tvar w2 NotAWriter\n\n\tfmt.Fprint(w, \"abc\", \"de\")   //@ diag(`Use fmt.Fprint`)\n\tfmt.Fprintf(w, \"%T\", w)      //@ diag(`Use fmt.Fprintf`)\n\tfmt.Fprintln(w, \"abc\", \"de\") //@ diag(`Use fmt.Fprintln`)\n\n\tw2.Write([]byte(fmt.Sprint(\"abc\", \"de\")))\n}\n\nfunc fn2() {\n\tbuf := new(bytes.Buffer)\n\tvar sw io.StringWriter\n\n\tfmt.Fprint(buf, \"abc\", \"de\")   //@ diag(`Use fmt.Fprint`)\n\tfmt.Fprintf(buf, \"%T\", 0)      //@ diag(`Use fmt.Fprintf`)\n\tfmt.Fprintln(buf, \"abc\", \"de\") //@ diag(`Use fmt.Fprintln`)\n\n\t// We can't suggest fmt.Fprint here. We don't know if sw implements io.Writer.\n\tsw.WriteString(fmt.Sprint(\"abc\", \"de\"))\n\tsw.WriteString(fmt.Sprintf(\"%T\", 0))\n\tsw.WriteString(fmt.Sprintln(\"abc\", \"de\"))\n}\n\nfunc fn3() {\n\tvar buf bytes.Buffer\n\tfmt.Fprint(&buf, \"abc\", \"de\") //@ diag(`Use fmt.Fprint`)\n}\n"
  },
  {
    "path": "sarif/sarif.go",
    "content": "package sarif\n\nconst Version = \"2.1.0\"\nconst Schema = \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\"\n\ntype Log struct {\n\tVersion string `json:\"version\"`\n\tSchema  string `json:\"$schema\"`\n\tRuns    []Run  `json:\"runs\"`\n}\n\ntype Run struct {\n\tTool        Tool         `json:\"tool\"`\n\tResults     []Result     `json:\"results,omitempty\"`\n\tInvocations []Invocation `json:\"invocations,omitempty\"`\n\tArtifacts   []Artifact   `json:\"artifacts,omitempty\"`\n}\n\ntype Artifact struct {\n\tLocation       ArtifactLocation `json:\"location\"`\n\tLength         int              `json:\"length\"`\n\tSourceLanguage string           `json:\"sourceLanguage\"`\n\tRoles          []string         `json:\"roles\"`\n\tEncoding       string           `json:\"encoding\"`\n}\n\nconst (\n\tAnalysisTarget = \"analysisTarget\"\n\tUTF8           = \"UTF-8\"\n\tFail           = \"fail\"\n\tWarning        = \"warning\"\n\tError          = \"error\"\n\tNote           = \"note\"\n\tNone           = \"none\"\n)\n\ntype Hash struct {\n\tSha256 string `json:\"sha-256\"`\n}\n\ntype Tool struct {\n\tDriver ToolComponent `json:\"driver\"`\n}\n\ntype Invocation struct {\n\tCommandLine         string           `json:\"commandLine,omitempty\"`\n\tArguments           []string         `json:\"arguments,omitempty\"`\n\tWorkingDirectory    ArtifactLocation `json:\"workingDirectory,omitzero\"`\n\tExecutionSuccessful bool             `json:\"executionSuccessful\"`\n}\n\ntype ToolComponent struct {\n\tName            string                `json:\"name,omitempty\"`\n\tVersion         string                `json:\"version,omitempty\"`\n\tSemanticVersion string                `json:\"semanticVersion,omitempty\"`\n\tInformationURI  string                `json:\"informationUri,omitempty\"`\n\tRules           []ReportingDescriptor `json:\"rules,omitempty\"`\n}\n\ntype ReportingDescriptor struct {\n\tID               string  `json:\"id\"`\n\tShortDescription Message `json:\"shortDescription\"`\n\t// FullDescription  Message `json:\"fullDescription\"`\n\tHelp                 Message                `json:\"help\"`\n\tHelpURI              string                 `json:\"helpUri,omitempty\"`\n\tDefaultConfiguration ReportingConfiguration `json:\"defaultConfiguration\"`\n}\n\ntype ReportingConfiguration struct {\n\tEnabled    bool           `json:\"enabled\"`\n\tLevel      string         `json:\"level,omitempty\"`\n\tParameters map[string]any `json:\"parameters,omitempty\"`\n}\n\ntype Result struct {\n\tRuleID string `json:\"ruleId\"`\n\t// RuleIndex        int        `json:\"ruleIndex\"`\n\tKind             string        `json:\"kind\"`\n\tLevel            string        `json:\"level,omitempty\"`\n\tMessage          Message       `json:\"message\"`\n\tLocations        []Location    `json:\"locations,omitempty\"`\n\tRelatedLocations []Location    `json:\"relatedLocations,omitempty\"`\n\tFixes            []Fix         `json:\"fixes,omitempty\"`\n\tSuppressions     []Suppression `json:\"suppressions\"`\n}\n\ntype Suppression struct {\n\tKind          string `json:\"kind\"`\n\tJustification string `json:\"justification\"`\n}\n\ntype Fix struct {\n\tDescription     Message          `json:\"description\"`\n\tArtifactChanges []ArtifactChange `json:\"artifactChanges\"`\n}\n\ntype ArtifactChange struct {\n\tArtifactLocation ArtifactLocation `json:\"artifactLocation\"`\n\tReplacements     []Replacement    `json:\"replacements\"`\n}\n\ntype Replacement struct {\n\tDeletedRegion   Region          `json:\"deletedRegion\"`\n\tInsertedContent ArtifactContent `json:\"insertedContent\"`\n}\n\ntype ArtifactContent struct {\n\tText string `json:\"text\"`\n}\n\ntype Message struct {\n\tText     string `json:\"text,omitempty\"`\n\tMarkdown string `json:\"markdown,omitempty\"`\n}\n\ntype Location struct {\n\tID               int              `json:\"id,omitempty\"`\n\tMessage          *Message         `json:\"message,omitempty\"`\n\tPhysicalLocation PhysicalLocation `json:\"physicalLocation\"`\n}\n\ntype PhysicalLocation struct {\n\tArtifactLocation ArtifactLocation `json:\"artifactLocation\"`\n\tRegion           Region           `json:\"region\"`\n}\n\ntype ArtifactLocation struct {\n\tURI       string `json:\"uri,omitempty\"`\n\tIndex     int    `json:\"index,omitempty\"`\n\tURIBaseID string `json:\"uriBaseId,omitempty\"`\n}\n\ntype Region struct {\n\tStartLine   int `json:\"startLine\"`\n\tStartColumn int `json:\"startColumn\"`\n\tEndLine     int `json:\"endLine,omitempty\"`\n\tEndColumn   int `json:\"endColumn,omitempty\"`\n}\n"
  },
  {
    "path": "simple/analysis.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage simple\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/simple/s1000\"\n\t\"honnef.co/go/tools/simple/s1001\"\n\t\"honnef.co/go/tools/simple/s1002\"\n\t\"honnef.co/go/tools/simple/s1003\"\n\t\"honnef.co/go/tools/simple/s1004\"\n\t\"honnef.co/go/tools/simple/s1005\"\n\t\"honnef.co/go/tools/simple/s1006\"\n\t\"honnef.co/go/tools/simple/s1007\"\n\t\"honnef.co/go/tools/simple/s1008\"\n\t\"honnef.co/go/tools/simple/s1009\"\n\t\"honnef.co/go/tools/simple/s1010\"\n\t\"honnef.co/go/tools/simple/s1011\"\n\t\"honnef.co/go/tools/simple/s1012\"\n\t\"honnef.co/go/tools/simple/s1016\"\n\t\"honnef.co/go/tools/simple/s1017\"\n\t\"honnef.co/go/tools/simple/s1018\"\n\t\"honnef.co/go/tools/simple/s1019\"\n\t\"honnef.co/go/tools/simple/s1020\"\n\t\"honnef.co/go/tools/simple/s1021\"\n\t\"honnef.co/go/tools/simple/s1023\"\n\t\"honnef.co/go/tools/simple/s1024\"\n\t\"honnef.co/go/tools/simple/s1025\"\n\t\"honnef.co/go/tools/simple/s1028\"\n\t\"honnef.co/go/tools/simple/s1029\"\n\t\"honnef.co/go/tools/simple/s1030\"\n\t\"honnef.co/go/tools/simple/s1031\"\n\t\"honnef.co/go/tools/simple/s1032\"\n\t\"honnef.co/go/tools/simple/s1033\"\n\t\"honnef.co/go/tools/simple/s1034\"\n\t\"honnef.co/go/tools/simple/s1035\"\n\t\"honnef.co/go/tools/simple/s1036\"\n\t\"honnef.co/go/tools/simple/s1037\"\n\t\"honnef.co/go/tools/simple/s1038\"\n\t\"honnef.co/go/tools/simple/s1039\"\n\t\"honnef.co/go/tools/simple/s1040\"\n)\n\nvar Analyzers = []*lint.Analyzer{\n\ts1000.SCAnalyzer,\n\ts1001.SCAnalyzer,\n\ts1002.SCAnalyzer,\n\ts1003.SCAnalyzer,\n\ts1004.SCAnalyzer,\n\ts1005.SCAnalyzer,\n\ts1006.SCAnalyzer,\n\ts1007.SCAnalyzer,\n\ts1008.SCAnalyzer,\n\ts1009.SCAnalyzer,\n\ts1010.SCAnalyzer,\n\ts1011.SCAnalyzer,\n\ts1012.SCAnalyzer,\n\ts1016.SCAnalyzer,\n\ts1017.SCAnalyzer,\n\ts1018.SCAnalyzer,\n\ts1019.SCAnalyzer,\n\ts1020.SCAnalyzer,\n\ts1021.SCAnalyzer,\n\ts1023.SCAnalyzer,\n\ts1024.SCAnalyzer,\n\ts1025.SCAnalyzer,\n\ts1028.SCAnalyzer,\n\ts1029.SCAnalyzer,\n\ts1030.SCAnalyzer,\n\ts1031.SCAnalyzer,\n\ts1032.SCAnalyzer,\n\ts1033.SCAnalyzer,\n\ts1034.SCAnalyzer,\n\ts1035.SCAnalyzer,\n\ts1036.SCAnalyzer,\n\ts1037.SCAnalyzer,\n\ts1038.SCAnalyzer,\n\ts1039.SCAnalyzer,\n\ts1040.SCAnalyzer,\n}\n"
  },
  {
    "path": "simple/doc.go",
    "content": "//go:generate go run ../generate.go\n\n// Package simple contains analyzes that simplify code.\n// All suggestions made by these analyzes are intended to result in objectively simpler code,\n// and following their advice is recommended.\npackage simple\n"
  },
  {
    "path": "simple/s1000/s1000.go",
    "content": "package s1000\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1000\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Use plain channel send or receive instead of single-case select`,\n\t\tText: `Select statements with a single case can be replaced with a simple\nsend or receive.`,\n\t\tBefore: `\nselect {\ncase x := <-ch:\n    fmt.Println(x)\n}`,\n\t\tAfter: `\nx := <-ch\nfmt.Println(x)\n`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckSingleCaseSelectQ1 = pattern.MustParse(`\n\t\t(ForStmt\n\t\t\tnil nil nil\n\t\t\tselect@(SelectStmt\n\t\t\t\t(CommClause\n\t\t\t\t\t(Or\n\t\t\t\t\t\t(UnaryExpr \"<-\" _)\n\t\t\t\t\t\t(AssignStmt _ _ (UnaryExpr \"<-\" _)))\n\t\t\t\t\t_)))`)\n\tcheckSingleCaseSelectQ2 = pattern.MustParse(`(SelectStmt (CommClause _ _))`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tseen := map[ast.Node]struct{}{}\n\tfn := func(node ast.Node) {\n\t\tif m, ok := code.Match(pass, checkSingleCaseSelectQ1, node); ok {\n\t\t\tseen[m.State[\"select\"].(ast.Node)] = struct{}{}\n\t\t\treport.Report(pass, node, \"should use for range instead of for { select {} }\", report.FilterGenerated())\n\t\t} else if _, ok := code.Match(pass, checkSingleCaseSelectQ2, node); ok {\n\t\t\tif _, ok := seen[node]; !ok {\n\t\t\t\treport.Report(pass, node, \"should use a simple channel send/receive instead of select with a single case\",\n\t\t\t\t\treport.ShortRange(),\n\t\t\t\t\treport.FilterGenerated())\n\t\t\t}\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.SelectStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1000/s1000_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1000\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1000/testdata/go1.0/CheckSingleCaseSelect/single-case-select.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar ch chan int\n\tselect { //@ diag(`should use a simple channel send`)\n\tcase <-ch:\n\t}\nouter:\n\tfor { //@ diag(`should use for range`)\n\t\tselect {\n\t\tcase <-ch:\n\t\t\tbreak outer\n\t\t}\n\t}\n\n\tfor { //@ diag(`should use for range`)\n\t\tselect {\n\t\tcase x := <-ch:\n\t\t\t_ = x\n\t\t}\n\t}\n\n\tfor {\n\t\tselect { //@ diag(`should use a simple channel send`)\n\t\tcase ch <- 0:\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "simple/s1001/s1001.go",
    "content": "package s1001\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1001\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Replace for loop with call to copy`,\n\t\tText: `\nUse \\'copy()\\' for copying elements from one slice to another. For\narrays of identical size, you can use simple assignment.`,\n\t\tBefore: `\nfor i, x := range src {\n    dst[i] = x\n}`,\n\t\tAfter: `copy(dst, src)`,\n\t\tSince: \"2017.1\",\n\t\t// MergeIfAll because the types of src and dst might be different under different build tags.\n\t\t// You shouldn't write code like that…\n\t\tMergeIf: lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckLoopCopyQ = pattern.MustParse(`\n\t\t(Or\n\t\t\t(RangeStmt\n\t\t\t\tkey@(Ident _) value@(Ident _) \":=\" src\n\t\t\t\t[(AssignStmt (IndexExpr dst key) \"=\" value)])\n\t\t\t(RangeStmt\n\t\t\t\tkey@(Ident _) nil \":=\" src\n\t\t\t\t[(AssignStmt (IndexExpr dst key) \"=\" (IndexExpr src key))])\n\t\t\t(ForStmt\n\t\t\t\t(AssignStmt key@(Ident _) \":=\" (IntegerLiteral \"0\"))\n\t\t\t\t(BinaryExpr key \"<\" (CallExpr (Symbol \"len\") [src]))\n\t\t\t\t(IncDecStmt key \"++\")\n\t\t\t\t[(AssignStmt (IndexExpr dst key) \"=\" (IndexExpr src key))]))`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// TODO revisit once range doesn't require a structural type\n\n\tisInvariant := func(k, v types.Object, node ast.Expr) bool {\n\t\tif code.MayHaveSideEffects(pass, node, nil) {\n\t\t\treturn false\n\t\t}\n\t\tinvariant := true\n\t\tast.Inspect(node, func(node ast.Node) bool {\n\t\t\tif node, ok := node.(*ast.Ident); ok {\n\t\t\t\tobj := pass.TypesInfo.ObjectOf(node)\n\t\t\t\tif obj == k || obj == v {\n\t\t\t\t\t// don't allow loop bodies like 'a[i][i] = v'\n\t\t\t\t\tinvariant = false\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t\treturn invariant\n\t}\n\n\tvar elType func(T types.Type) (el types.Type, isArray bool, isArrayPointer bool, ok bool)\n\telType = func(T types.Type) (el types.Type, isArray bool, isArrayPointer bool, ok bool) {\n\t\tswitch typ := T.Underlying().(type) {\n\t\tcase *types.Slice:\n\t\t\treturn typ.Elem(), false, false, true\n\t\tcase *types.Array:\n\t\t\treturn typ.Elem(), true, false, true\n\t\tcase *types.Pointer:\n\t\t\tel, isArray, _, ok = elType(typ.Elem())\n\t\t\treturn el, isArray, true, ok\n\t\tdefault:\n\t\t\treturn nil, false, false, false\n\t\t}\n\t}\n\n\tfor node, m := range code.Matches(pass, checkLoopCopyQ) {\n\t\tsrc := m.State[\"src\"].(ast.Expr)\n\t\tdst := m.State[\"dst\"].(ast.Expr)\n\n\t\tk := pass.TypesInfo.ObjectOf(m.State[\"key\"].(*ast.Ident))\n\t\tvar v types.Object\n\t\tif value, ok := m.State[\"value\"]; ok {\n\t\t\tv = pass.TypesInfo.ObjectOf(value.(*ast.Ident))\n\t\t}\n\t\tif !isInvariant(k, v, dst) {\n\t\t\tcontinue\n\t\t}\n\t\tif !isInvariant(k, v, src) {\n\t\t\t// For example: 'for i := range foo()'\n\t\t\tcontinue\n\t\t}\n\n\t\tTsrc := pass.TypesInfo.TypeOf(src)\n\t\tTdst := pass.TypesInfo.TypeOf(dst)\n\t\tTsrcElem, TsrcArray, TsrcPointer, ok := elType(Tsrc)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tif TsrcPointer {\n\t\t\tTsrc = Tsrc.Underlying().(*types.Pointer).Elem()\n\t\t}\n\t\tTdstElem, TdstArray, TdstPointer, ok := elType(Tdst)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tif TdstPointer {\n\t\t\tTdst = Tdst.Underlying().(*types.Pointer).Elem()\n\t\t}\n\n\t\tif !types.Identical(TsrcElem, TdstElem) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif TsrcArray && TdstArray && types.Identical(Tsrc, Tdst) {\n\t\t\tif TsrcPointer {\n\t\t\t\tsrc = &ast.StarExpr{\n\t\t\t\t\tX: src,\n\t\t\t\t}\n\t\t\t}\n\t\t\tif TdstPointer {\n\t\t\t\tdst = &ast.StarExpr{\n\t\t\t\t\tX: dst,\n\t\t\t\t}\n\t\t\t}\n\t\t\tr := &ast.AssignStmt{\n\t\t\t\tLhs: []ast.Expr{dst},\n\t\t\t\tRhs: []ast.Expr{src},\n\t\t\t\tTok: token.ASSIGN,\n\t\t\t}\n\n\t\t\treport.Report(pass, node, \"should copy arrays using assignment instead of using a loop\",\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.ShortRange(),\n\t\t\t\treport.Fixes(edit.Fix(\"Replace loop with assignment\", edit.ReplaceWithNode(pass.Fset, node, r))))\n\t\t} else {\n\t\t\ttv, err := types.Eval(pass.Fset, pass.Pkg, node.Pos(), \"copy\")\n\t\t\tif err == nil && tv.IsBuiltin() {\n\t\t\t\tto := \"to\"\n\t\t\t\tfrom := \"from\"\n\t\t\t\tsrc := m.State[\"src\"].(ast.Expr)\n\t\t\t\tif TsrcArray {\n\t\t\t\t\tfrom = \"from[:]\"\n\t\t\t\t\tsrc = &ast.SliceExpr{\n\t\t\t\t\t\tX: src,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdst := m.State[\"dst\"].(ast.Expr)\n\t\t\t\tif TdstArray {\n\t\t\t\t\tto = \"to[:]\"\n\t\t\t\t\tdst = &ast.SliceExpr{\n\t\t\t\t\t\tX: dst,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tr := &ast.CallExpr{\n\t\t\t\t\tFun:  &ast.Ident{Name: \"copy\"},\n\t\t\t\t\tArgs: []ast.Expr{dst, src},\n\t\t\t\t}\n\t\t\t\topts := []report.Option{\n\t\t\t\t\treport.ShortRange(),\n\t\t\t\t\treport.FilterGenerated(),\n\t\t\t\t\treport.Fixes(edit.Fix(\"Replace loop with call to copy()\", edit.ReplaceWithNode(pass.Fset, node, r))),\n\t\t\t\t}\n\t\t\t\treport.Report(pass, node, fmt.Sprintf(\"should use copy(%s, %s) instead of a loop\", to, from), opts...)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1001/s1001_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1001\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1001/testdata/go1.0/CheckLoopCopy/copy.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar b1, b2 []byte\n\tfor i, v := range b1 { //@ diag(`should use copy(to, from)`)\n\t\tb2[i] = v\n\t}\n\n\tfor i := range b1 { //@ diag(`should use copy`)\n\t\tb2[i] = b1[i]\n\t}\n\n\ttype T [][16]byte\n\tvar a T\n\tb := make([]interface{}, len(a))\n\tfor i := range b {\n\t\tb[i] = a[i]\n\t}\n\n\tvar b3, b4 []*byte\n\tfor i := range b3 { //@ diag(`should use copy`)\n\t\tb4[i] = b3[i]\n\t}\n\n\tvar m map[int]byte\n\tfor i, v := range b1 {\n\t\tm[i] = v\n\t}\n\n}\n\nfunc src() []interface{} { return nil }\n\nfunc fn1() {\n\t// Don't flag this, the source is dynamic\n\tvar dst []interface{}\n\tfor i := range src() {\n\t\tdst[i] = src()[i]\n\t}\n}\n\nfunc fn2() {\n\ttype T struct {\n\t\tb []byte\n\t}\n\n\tvar src []byte\n\tvar dst T\n\tfor i, v := range src { //@ diag(`should use copy`)\n\t\tdst.b[i] = v\n\t}\n}\n\nfunc fn3() {\n\tvar src []byte\n\tvar dst [][]byte\n\tfor i, v := range src { //@ diag(`should use copy`)\n\t\tdst[0][i] = v\n\t}\n\tfor i, v := range src {\n\t\t// Don't flag, destination depends on loop variable\n\t\tdst[i][i] = v\n\t}\n\tfor i, v := range src {\n\t\t// Don't flag, destination depends on loop variable\n\t\tdst[v][i] = v\n\t}\n}\n\nfunc fn4() {\n\tvar b []byte\n\tvar a1 [5]byte\n\tvar a2 [10]byte\n\tvar a3 [5]byte\n\n\tfor i := range b { //@ diag(`should use copy(to[:], from)`)\n\t\ta1[i] = b[i]\n\t}\n\tfor i := range a1 { //@ diag(`should use copy(to, from[:])`)\n\t\tb[i] = a1[i]\n\t}\n\tfor i := range a1 { //@ diag(`should use copy(to[:], from[:])`)\n\t\ta2[i] = a1[i]\n\t}\n\tfor i := range a1 { //@ diag(`should copy arrays using assignment`)\n\t\ta3[i] = a1[i]\n\t}\n\n\ta1p := &a1\n\ta2p := &a2\n\ta3p := &a3\n\tfor i := range b { //@ diag(`should use copy`)\n\t\ta1p[i] = b[i]\n\t}\n\tfor i := range a1p { //@ diag(`should use copy`)\n\t\tb[i] = a1p[i]\n\t}\n\tfor i := range a1p { //@ diag(`should use copy`)\n\t\ta2p[i] = a1p[i]\n\t}\n\tfor i := range a1p { //@ diag(`should copy arrays using assignment`)\n\t\ta3p[i] = a1p[i]\n\t}\n\n\tfor i := range a1 { //@ diag(`should use copy`)\n\t\ta2p[i] = a1[i]\n\t}\n\tfor i := range a1 { //@ diag(`should copy arrays using assignment`)\n\t\ta3p[i] = a1[i]\n\t}\n\tfor i := range a1p { //@ diag(`should use copy`)\n\t\ta2[i] = a1p[i]\n\t}\n\tfor i := range a1p { //@ diag(`should copy arrays using assignment`)\n\t\ta3[i] = a1p[i]\n\t}\n}\n\nfunc fn5() {\n\tvar src, dst []byte\n\tfor i := 0; i < len(src); i++ { //@ diag(`should use copy`)\n\t\tdst[i] = src[i]\n\t}\n\n\tlen := func([]byte) int { return 0 }\n\tfor i := 0; i < len(src); i++ {\n\t\tdst[i] = src[i]\n\t}\n}\n\nfunc fn6() {\n\tvar src, dst []byte\n\tcopy := func() {}\n\t_ = copy\n\tfor i, v := range src {\n\t\tdst[i] = v\n\t}\n}\n"
  },
  {
    "path": "simple/s1001/testdata/go1.0/CheckLoopCopy/copy.go.golden",
    "content": "package pkg\n\nfunc fn() {\n\tvar b1, b2 []byte\n\tcopy(b2, b1)\n\n\tcopy(b2, b1)\n\n\ttype T [][16]byte\n\tvar a T\n\tb := make([]interface{}, len(a))\n\tfor i := range b {\n\t\tb[i] = a[i]\n\t}\n\n\tvar b3, b4 []*byte\n\tcopy(b4, b3)\n\n\tvar m map[int]byte\n\tfor i, v := range b1 {\n\t\tm[i] = v\n\t}\n\n}\n\nfunc src() []interface{} { return nil }\n\nfunc fn1() {\n\t// Don't flag this, the source is dynamic\n\tvar dst []interface{}\n\tfor i := range src() {\n\t\tdst[i] = src()[i]\n\t}\n}\n\nfunc fn2() {\n\ttype T struct {\n\t\tb []byte\n\t}\n\n\tvar src []byte\n\tvar dst T\n\tcopy(dst.b, src)\n}\n\nfunc fn3() {\n\tvar src []byte\n\tvar dst [][]byte\n\tcopy(dst[0], src)\n\tfor i, v := range src {\n\t\t// Don't flag, destination depends on loop variable\n\t\tdst[i][i] = v\n\t}\n\tfor i, v := range src {\n\t\t// Don't flag, destination depends on loop variable\n\t\tdst[v][i] = v\n\t}\n}\n\nfunc fn4() {\n\tvar b []byte\n\tvar a1 [5]byte\n\tvar a2 [10]byte\n\tvar a3 [5]byte\n\n\tcopy(a1[:], b)\n\tcopy(b, a1[:])\n\tcopy(a2[:], a1[:])\n\ta3 = a1\n\n\ta1p := &a1\n\ta2p := &a2\n\ta3p := &a3\n\tcopy(a1p[:], b)\n\tcopy(b, a1p[:])\n\tcopy(a2p[:], a1p[:])\n\t*a3p = *a1p\n\n\tcopy(a2p[:], a1[:])\n\t*a3p = a1\n\tcopy(a2[:], a1p[:])\n\ta3 = *a1p\n}\n\nfunc fn5() {\n\tvar src, dst []byte\n\tcopy(dst, src)\n\n\tlen := func([]byte) int { return 0 }\n\tfor i := 0; i < len(src); i++ {\n\t\tdst[i] = src[i]\n\t}\n}\n\nfunc fn6() {\n\tvar src, dst []byte\n\tcopy := func() {}\n\t_ = copy\n\tfor i, v := range src {\n\t\tdst[i] = v\n\t}\n}\n"
  },
  {
    "path": "simple/s1001/testdata/go1.18/CheckLoopCopy/copy_generics.go",
    "content": "package pkg\n\nfunc tpfn[T any]() {\n\tvar b1, b2 []T\n\tfor i, v := range b1 { //@ diag(`should use copy`)\n\t\tb2[i] = v\n\t}\n\n\tfor i := range b1 { //@ diag(`should use copy`)\n\t\tb2[i] = b1[i]\n\t}\n\n\ttype T2 [][16]T\n\tvar a T2\n\tb := make([]any, len(a))\n\tfor i := range b {\n\t\tb[i] = a[i]\n\t}\n\n\tvar b3, b4 []*T\n\tfor i := range b3 { //@ diag(`should use copy`)\n\t\tb4[i] = b3[i]\n\t}\n\n\tvar m map[int]T\n\tfor i, v := range b1 {\n\t\tm[i] = v\n\t}\n\n}\n\nfunc tpsrc[T any]() []T { return nil }\n\nfunc tpfn1() {\n\t// Don't flag this, the source is dynamic\n\tvar dst []any\n\tfor i := range tpsrc[any]() {\n\t\tdst[i] = tpsrc[any]()[i]\n\t}\n}\n\nfunc tpfn2[T any]() {\n\ttype T2 struct {\n\t\tb []T\n\t}\n\n\tvar src []T\n\tvar dst T2\n\tfor i, v := range src { //@ diag(`should use copy`)\n\t\tdst.b[i] = v\n\t}\n}\n\nfunc tpfn3[T any]() {\n\tvar src []T\n\tvar dst [][]T\n\tfor i, v := range src { //@ diag(`should use copy`)\n\t\tdst[0][i] = v\n\t}\n\tfor i, v := range src {\n\t\t// Don't flag, destination depends on loop variable\n\t\tdst[i][i] = v\n\t}\n}\n\nfunc tpfn4[T any]() {\n\tvar b []T\n\tvar a1 [5]T\n\tvar a2 [10]T\n\tvar a3 [5]T\n\n\tfor i := range b { //@ diag(`should use copy`)\n\t\ta1[i] = b[i]\n\t}\n\tfor i := range a1 { //@ diag(`should use copy`)\n\t\tb[i] = a1[i]\n\t}\n\tfor i := range a1 { //@ diag(`should use copy`)\n\t\ta2[i] = a1[i]\n\t}\n\tfor i := range a1 { //@ diag(`should copy arrays using assignment`)\n\t\ta3[i] = a1[i]\n\t}\n\n\ta1p := &a1\n\ta2p := &a2\n\ta3p := &a3\n\tfor i := range b { //@ diag(`should use copy`)\n\t\ta1p[i] = b[i]\n\t}\n\tfor i := range a1p { //@ diag(`should use copy`)\n\t\tb[i] = a1p[i]\n\t}\n\tfor i := range a1p { //@ diag(`should use copy`)\n\t\ta2p[i] = a1p[i]\n\t}\n\tfor i := range a1p { //@ diag(`should copy arrays using assignment`)\n\t\ta3p[i] = a1p[i]\n\t}\n\n\tfor i := range a1 { //@ diag(`should use copy`)\n\t\ta2p[i] = a1[i]\n\t}\n\tfor i := range a1 { //@ diag(`should copy arrays using assignment`)\n\t\ta3p[i] = a1[i]\n\t}\n\tfor i := range a1p { //@ diag(`should use copy`)\n\t\ta2[i] = a1p[i]\n\t}\n\tfor i := range a1p { //@ diag(`should copy arrays using assignment`)\n\t\ta3[i] = a1p[i]\n\t}\n}\n\nfunc tpfn5[T any]() {\n\tvar src, dst []T\n\tfor i := 0; i < len(src); i++ { //@ diag(`should use copy`)\n\t\tdst[i] = src[i]\n\t}\n\n\tlen := func([]T) int { return 0 }\n\tfor i := 0; i < len(src); i++ {\n\t\tdst[i] = src[i]\n\t}\n}\n"
  },
  {
    "path": "simple/s1001/testdata/go1.18/CheckLoopCopy/copy_generics.go.golden",
    "content": "package pkg\n\nfunc tpfn[T any]() {\n\tvar b1, b2 []T\n\tcopy(b2, b1)\n\n\tcopy(b2, b1)\n\n\ttype T2 [][16]T\n\tvar a T2\n\tb := make([]any, len(a))\n\tfor i := range b {\n\t\tb[i] = a[i]\n\t}\n\n\tvar b3, b4 []*T\n\tcopy(b4, b3)\n\n\tvar m map[int]T\n\tfor i, v := range b1 {\n\t\tm[i] = v\n\t}\n\n}\n\nfunc tpsrc[T any]() []T { return nil }\n\nfunc tpfn1() {\n\t// Don't flag this, the source is dynamic\n\tvar dst []any\n\tfor i := range tpsrc[any]() {\n\t\tdst[i] = tpsrc[any]()[i]\n\t}\n}\n\nfunc tpfn2[T any]() {\n\ttype T2 struct {\n\t\tb []T\n\t}\n\n\tvar src []T\n\tvar dst T2\n\tcopy(dst.b, src)\n}\n\nfunc tpfn3[T any]() {\n\tvar src []T\n\tvar dst [][]T\n\tcopy(dst[0], src)\n\tfor i, v := range src {\n\t\t// Don't flag, destination depends on loop variable\n\t\tdst[i][i] = v\n\t}\n}\n\nfunc tpfn4[T any]() {\n\tvar b []T\n\tvar a1 [5]T\n\tvar a2 [10]T\n\tvar a3 [5]T\n\n\tcopy(a1[:], b)\n\tcopy(b, a1[:])\n\tcopy(a2[:], a1[:])\n\ta3 = a1\n\n\ta1p := &a1\n\ta2p := &a2\n\ta3p := &a3\n\tcopy(a1p[:], b)\n\tcopy(b, a1p[:])\n\tcopy(a2p[:], a1p[:])\n\t*a3p = *a1p\n\n\tcopy(a2p[:], a1[:])\n\t*a3p = a1\n\tcopy(a2[:], a1p[:])\n\ta3 = *a1p\n}\n\nfunc tpfn5[T any]() {\n\tvar src, dst []T\n\tcopy(dst, src)\n\n\tlen := func([]T) int { return 0 }\n\tfor i := 0; i < len(src); i++ {\n\t\tdst[i] = src[i]\n\t}\n}\n"
  },
  {
    "path": "simple/s1002/s1002.go",
    "content": "package s1002\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1002\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:  `Omit comparison with boolean constant`,\n\t\tBefore: `if x == true {}`,\n\t\tAfter:  `if x {}`,\n\t\tSince:  \"2017.1\",\n\t\t// MergeIfAll because 'true' might not be the builtin constant under all build tags.\n\t\t// You shouldn't write code like that…\n\t\tMergeIf: lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tif code.IsInTest(pass, node) {\n\t\t\treturn\n\t\t}\n\n\t\texpr := node.(*ast.BinaryExpr)\n\t\tif expr.Op != token.EQL && expr.Op != token.NEQ {\n\t\t\treturn\n\t\t}\n\t\tx := code.IsBoolConst(pass, expr.X)\n\t\ty := code.IsBoolConst(pass, expr.Y)\n\t\tif !x && !y {\n\t\t\treturn\n\t\t}\n\t\tvar other ast.Expr\n\t\tvar val bool\n\t\tif x {\n\t\t\tval = code.BoolConst(pass, expr.X)\n\t\t\tother = expr.Y\n\t\t} else {\n\t\t\tval = code.BoolConst(pass, expr.Y)\n\t\t\tother = expr.X\n\t\t}\n\n\t\tok := typeutil.All(pass.TypesInfo.TypeOf(other), func(term *types.Term) bool {\n\t\t\tbasic, ok := term.Type().Underlying().(*types.Basic)\n\t\t\treturn ok && basic.Kind() == types.Bool\n\t\t})\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\top := \"\"\n\t\tif (expr.Op == token.EQL && !val) || (expr.Op == token.NEQ && val) {\n\t\t\top = \"!\"\n\t\t}\n\t\tr := op + report.Render(pass, other)\n\t\tl1 := len(r)\n\t\tr = strings.TrimLeft(r, \"!\")\n\t\tif (l1-len(r))%2 == 1 {\n\t\t\tr = \"!\" + r\n\t\t}\n\t\treport.Report(pass, expr, fmt.Sprintf(\"should omit comparison to bool constant, can be simplified to %s\", r),\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(\"Simplify bool comparison\", edit.ReplaceWithString(expr, r))))\n\t}\n\tcode.Preorder(pass, fn, (*ast.BinaryExpr)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1002/s1002_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1002\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1002/testdata/go1.0/CheckIfBoolCmp/bool-cmp.go",
    "content": "package pkg\n\nfunc fn1() bool { return false }\nfunc fn2() bool { return false }\n\nfunc fn() {\n\ttype T bool\n\tvar x T\n\tconst t T = false\n\tif x == t {\n\t}\n\tif fn1() == true { //@ diag(`simplified to fn1()`)\n\t}\n\tif fn1() != true { //@ diag(`simplified to !fn1()`)\n\t}\n\tif fn1() == false { //@ diag(`simplified to !fn1()`)\n\t}\n\tif fn1() != false { //@ diag(`simplified to fn1()`)\n\t}\n\tif fn1() && (fn1() || fn1()) || (fn1() && fn1()) == true { //@ diag(`simplified to (fn1() && fn1())`)\n\t}\n\n\tif (fn1() && fn2()) == false { //@ diag(`simplified to !(fn1() && fn2())`)\n\t}\n\n\tvar y bool\n\tfor y != true { //@ diag(`simplified to !y`)\n\t}\n\tif !y == true { //@ diag(`simplified to !y`)\n\t}\n\tif !y == false { //@ diag(`simplified to y`)\n\t}\n\tif !y != true { //@ diag(`simplified to y`)\n\t}\n\tif !y != false { //@ diag(`simplified to !y`)\n\t}\n\tif !!y == false { //@ diag(`simplified to !y`)\n\t}\n\tif !!!y == false { //@ diag(`simplified to y`)\n\t}\n\tif !!y == true { //@ diag(`simplified to y`)\n\t}\n\tif !!!y == true { //@ diag(`simplified to !y`)\n\t}\n\tif !!y != true { //@ diag(`simplified to !y`)\n\t}\n\tif !!!y != true { //@ diag(`simplified to y`)\n\t}\n\tif !y == !false { // not matched because we expect true/false on one side, not !false\n\t}\n\n\tvar z interface{}\n\tif z == true {\n\t}\n}\n"
  },
  {
    "path": "simple/s1002/testdata/go1.0/CheckIfBoolCmp/bool-cmp.go.golden",
    "content": "package pkg\n\nfunc fn1() bool { return false }\nfunc fn2() bool { return false }\n\nfunc fn() {\n\ttype T bool\n\tvar x T\n\tconst t T = false\n\tif x == t {\n\t}\n\tif fn1() { //@ diag(`simplified to fn1()`)\n\t}\n\tif !fn1() { //@ diag(`simplified to !fn1()`)\n\t}\n\tif !fn1() { //@ diag(`simplified to !fn1()`)\n\t}\n\tif fn1() { //@ diag(`simplified to fn1()`)\n\t}\n\tif fn1() && (fn1() || fn1()) || (fn1() && fn1()) { //@ diag(`simplified to (fn1() && fn1())`)\n\t}\n\n\tif !(fn1() && fn2()) { //@ diag(`simplified to !(fn1() && fn2())`)\n\t}\n\n\tvar y bool\n\tfor !y { //@ diag(`simplified to !y`)\n\t}\n\tif !y { //@ diag(`simplified to !y`)\n\t}\n\tif y { //@ diag(`simplified to y`)\n\t}\n\tif y { //@ diag(`simplified to y`)\n\t}\n\tif !y { //@ diag(`simplified to !y`)\n\t}\n\tif !y { //@ diag(`simplified to !y`)\n\t}\n\tif y { //@ diag(`simplified to y`)\n\t}\n\tif y { //@ diag(`simplified to y`)\n\t}\n\tif !y { //@ diag(`simplified to !y`)\n\t}\n\tif !y { //@ diag(`simplified to !y`)\n\t}\n\tif y { //@ diag(`simplified to y`)\n\t}\n\tif !y == !false { // not matched because we expect true/false on one side, not !false\n\t}\n\n\tvar z interface{}\n\tif z == true {\n\t}\n}\n"
  },
  {
    "path": "simple/s1002/testdata/go1.0/CheckIfBoolCmp/bool-cmp_test.go",
    "content": "package pkg\n\nimport \"testing\"\n\nfunc TestFoo(t *testing.T) {\n\tif fn1() == true {\n\t}\n}\n"
  },
  {
    "path": "simple/s1002/testdata/go1.18/CheckIfBoolCmp/bool-cmp_generics.go",
    "content": "package pkg\n\nfunc tpfn1[T any]() T { var zero T; return zero }\n\nfunc tpfn() {\n\tif tpfn1[bool]() == true { //@ diag(`simplified to tpfn1[bool]()`)\n\t}\n\tif tpfn1[any]() == true {\n\t}\n}\n\nfunc tpfn2[T bool](x T) {\n\tif x == true { //@ diag(`omit comparison to bool constant`)\n\t}\n}\n\nfunc tpfn3[T ~bool](x T) {\n\tif x == true { //@ diag(`omit comparison to bool constant`)\n\t}\n}\n\ntype MyBool bool\n\nfunc tpfn4[T bool | MyBool](x T) {\n\tif x == true { //@ diag(`omit comparison to bool constant`)\n\t}\n}\n"
  },
  {
    "path": "simple/s1002/testdata/go1.18/CheckIfBoolCmp/bool-cmp_generics.go.golden",
    "content": "package pkg\n\nfunc tpfn1[T any]() T { var zero T; return zero }\n\nfunc tpfn() {\n\tif tpfn1[bool]() { //@ diag(`simplified to tpfn1[bool]()`)\n\t}\n\tif tpfn1[any]() == true {\n\t}\n}\n\nfunc tpfn2[T bool](x T) {\n\tif x { //@ diag(`omit comparison to bool constant`)\n\t}\n}\n\nfunc tpfn3[T ~bool](x T) {\n\tif x { //@ diag(`omit comparison to bool constant`)\n\t}\n}\n\ntype MyBool bool\n\nfunc tpfn4[T bool | MyBool](x T) {\n\tif x { //@ diag(`omit comparison to bool constant`)\n\t}\n}\n"
  },
  {
    "path": "simple/s1003/s1003.go",
    "content": "package s1003\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1003\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:   `Replace call to \\'strings.Index\\' with \\'strings.Contains\\'`,\n\t\tBefore:  `if strings.Index(x, y) != -1 {}`,\n\t\tAfter:   `if strings.Contains(x, y) {}`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// map of value to token to bool value\n\tallowed := map[int64]map[token.Token]bool{\n\t\t-1: {token.GTR: true, token.NEQ: true, token.EQL: false},\n\t\t0:  {token.GEQ: true, token.LSS: false},\n\t}\n\tfn := func(node ast.Node) {\n\t\texpr := node.(*ast.BinaryExpr)\n\t\tswitch expr.Op {\n\t\tcase token.GEQ, token.GTR, token.NEQ, token.LSS, token.EQL:\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\n\t\tvalue, ok := code.ExprToInt(pass, expr.Y)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tallowedOps, ok := allowed[value]\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tb, ok := allowedOps[expr.Op]\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tcall, ok := expr.X.(*ast.CallExpr)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tsel, ok := call.Fun.(*ast.SelectorExpr)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tpkgIdent, ok := sel.X.(*ast.Ident)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tfunIdent := sel.Sel\n\t\tif pkgIdent.Name != \"strings\" && pkgIdent.Name != \"bytes\" {\n\t\t\treturn\n\t\t}\n\n\t\tvar r ast.Expr\n\t\tswitch funIdent.Name {\n\t\tcase \"IndexRune\":\n\t\t\tr = &ast.SelectorExpr{\n\t\t\t\tX:   pkgIdent,\n\t\t\t\tSel: &ast.Ident{Name: \"ContainsRune\"},\n\t\t\t}\n\t\tcase \"IndexAny\":\n\t\t\tr = &ast.SelectorExpr{\n\t\t\t\tX:   pkgIdent,\n\t\t\t\tSel: &ast.Ident{Name: \"ContainsAny\"},\n\t\t\t}\n\t\tcase \"Index\":\n\t\t\tr = &ast.SelectorExpr{\n\t\t\t\tX:   pkgIdent,\n\t\t\t\tSel: &ast.Ident{Name: \"Contains\"},\n\t\t\t}\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\n\t\tr = &ast.CallExpr{\n\t\t\tFun:  r,\n\t\t\tArgs: call.Args,\n\t\t}\n\t\tif !b {\n\t\t\tr = &ast.UnaryExpr{\n\t\t\t\tOp: token.NOT,\n\t\t\t\tX:  r,\n\t\t\t}\n\t\t}\n\n\t\treport.Report(pass, node, fmt.Sprintf(\"should use %s instead\", report.Render(pass, r)),\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(fmt.Sprintf(\"Simplify use of %s\", report.Render(pass, call.Fun)), edit.ReplaceWithNode(pass.Fset, node, r))))\n\t}\n\tcode.Preorder(pass, fn, (*ast.BinaryExpr)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1003/s1003_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1003\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1003/testdata/go1.0/CheckStringsContains/contains.go",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n)\n\nfunc fn() {\n\t_ = strings.IndexRune(\"\", 'x') > -1 //@ diag(` strings.ContainsRune`)\n\t_ = strings.IndexRune(\"\", 'x') >= 0 //@ diag(` strings.ContainsRune`)\n\t_ = strings.IndexRune(\"\", 'x') > 0\n\t_ = strings.IndexRune(\"\", 'x') >= -1\n\t_ = strings.IndexRune(\"\", 'x') != -1 //@ diag(` strings.ContainsRune`)\n\t_ = strings.IndexRune(\"\", 'x') == -1 //@ diag(`!strings.ContainsRune`)\n\t_ = strings.IndexRune(\"\", 'x') != 0\n\t_ = strings.IndexRune(\"\", 'x') < 0 //@ diag(`!strings.ContainsRune`)\n\n\t_ = strings.IndexAny(\"\", \"\") > -1 //@ diag(` strings.ContainsAny`)\n\t_ = strings.IndexAny(\"\", \"\") >= 0 //@ diag(` strings.ContainsAny`)\n\t_ = strings.IndexAny(\"\", \"\") > 0\n\t_ = strings.IndexAny(\"\", \"\") >= -1\n\t_ = strings.IndexAny(\"\", \"\") != -1 //@ diag(` strings.ContainsAny`)\n\t_ = strings.IndexAny(\"\", \"\") == -1 //@ diag(`!strings.ContainsAny`)\n\t_ = strings.IndexAny(\"\", \"\") != 0\n\t_ = strings.IndexAny(\"\", \"\") < 0 //@ diag(`!strings.ContainsAny`)\n\n\t_ = strings.Index(\"\", \"\") > -1 //@ diag(` strings.Contains`)\n\t_ = strings.Index(\"\", \"\") >= 0 //@ diag(` strings.Contains`)\n\t_ = strings.Index(\"\", \"\") > 0\n\t_ = strings.Index(\"\", \"\") >= -1\n\t_ = strings.Index(\"\", \"\") != -1 //@ diag(` strings.Contains`)\n\t_ = strings.Index(\"\", \"\") == -1 //@ diag(`!strings.Contains`)\n\t_ = strings.Index(\"\", \"\") != 0\n\t_ = strings.Index(\"\", \"\") < 0 //@ diag(`!strings.Contains`)\n\n\t_ = bytes.IndexRune(nil, 'x') > -1 //@ diag(` bytes.ContainsRune`)\n\t_ = bytes.IndexAny(nil, \"\") > -1   //@ diag(` bytes.ContainsAny`)\n\t_ = bytes.Index(nil, nil) > -1     //@ diag(` bytes.Contains`)\n}\n"
  },
  {
    "path": "simple/s1003/testdata/go1.0/CheckStringsContains/contains.go.golden",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n)\n\nfunc fn() {\n\t_ = strings.ContainsRune(\"\", 'x') //@ diag(` strings.ContainsRune`)\n\t_ = strings.ContainsRune(\"\", 'x') //@ diag(` strings.ContainsRune`)\n\t_ = strings.IndexRune(\"\", 'x') > 0\n\t_ = strings.IndexRune(\"\", 'x') >= -1\n\t_ = strings.ContainsRune(\"\", 'x')  //@ diag(` strings.ContainsRune`)\n\t_ = !strings.ContainsRune(\"\", 'x') //@ diag(`!strings.ContainsRune`)\n\t_ = strings.IndexRune(\"\", 'x') != 0\n\t_ = !strings.ContainsRune(\"\", 'x') //@ diag(`!strings.ContainsRune`)\n\n\t_ = strings.ContainsAny(\"\", \"\") //@ diag(` strings.ContainsAny`)\n\t_ = strings.ContainsAny(\"\", \"\") //@ diag(` strings.ContainsAny`)\n\t_ = strings.IndexAny(\"\", \"\") > 0\n\t_ = strings.IndexAny(\"\", \"\") >= -1\n\t_ = strings.ContainsAny(\"\", \"\")  //@ diag(` strings.ContainsAny`)\n\t_ = !strings.ContainsAny(\"\", \"\") //@ diag(`!strings.ContainsAny`)\n\t_ = strings.IndexAny(\"\", \"\") != 0\n\t_ = !strings.ContainsAny(\"\", \"\") //@ diag(`!strings.ContainsAny`)\n\n\t_ = strings.Contains(\"\", \"\") //@ diag(` strings.Contains`)\n\t_ = strings.Contains(\"\", \"\") //@ diag(` strings.Contains`)\n\t_ = strings.Index(\"\", \"\") > 0\n\t_ = strings.Index(\"\", \"\") >= -1\n\t_ = strings.Contains(\"\", \"\")  //@ diag(` strings.Contains`)\n\t_ = !strings.Contains(\"\", \"\") //@ diag(`!strings.Contains`)\n\t_ = strings.Index(\"\", \"\") != 0\n\t_ = !strings.Contains(\"\", \"\") //@ diag(`!strings.Contains`)\n\n\t_ = bytes.ContainsRune(nil, 'x') //@ diag(` bytes.ContainsRune`)\n\t_ = bytes.ContainsAny(nil, \"\")   //@ diag(` bytes.ContainsAny`)\n\t_ = bytes.Contains(nil, nil)     //@ diag(` bytes.Contains`)\n}\n"
  },
  {
    "path": "simple/s1004/s1004.go",
    "content": "package s1004\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1004\",\n\t\tRun:      CheckBytesCompare,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:   `Replace call to \\'bytes.Compare\\' with \\'bytes.Equal\\'`,\n\t\tBefore:  `if bytes.Compare(x, y) == 0 {}`,\n\t\tAfter:   `if bytes.Equal(x, y) {}`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckBytesCompareQ  = pattern.MustParse(`(BinaryExpr (CallExpr (Symbol \"bytes.Compare\") args) op@(Or \"==\" \"!=\") (IntegerLiteral \"0\"))`)\n\tcheckBytesCompareRe = pattern.MustParse(`(CallExpr (SelectorExpr (Ident \"bytes\") (Ident \"Equal\")) args)`)\n\tcheckBytesCompareRn = pattern.MustParse(`(UnaryExpr \"!\" (CallExpr (SelectorExpr (Ident \"bytes\") (Ident \"Equal\")) args))`)\n)\n\nfunc CheckBytesCompare(pass *analysis.Pass) (any, error) {\n\tif pass.Pkg.Path() == \"bytes\" || pass.Pkg.Path() == \"bytes_test\" {\n\t\t// the bytes package is free to use bytes.Compare as it sees fit\n\t\treturn nil, nil\n\t}\n\tfor node, m := range code.Matches(pass, checkBytesCompareQ) {\n\t\targs := report.RenderArgs(pass, m.State[\"args\"].([]ast.Expr))\n\t\tprefix := \"\"\n\t\tif m.State[\"op\"].(token.Token) == token.NEQ {\n\t\t\tprefix = \"!\"\n\t\t}\n\n\t\tvar fix analysis.SuggestedFix\n\t\tswitch tok := m.State[\"op\"].(token.Token); tok {\n\t\tcase token.EQL:\n\t\t\tfix = edit.Fix(\"Simplify use of bytes.Compare\", edit.ReplaceWithPattern(pass.Fset, node, checkBytesCompareRe, m.State))\n\t\tcase token.NEQ:\n\t\t\tfix = edit.Fix(\"Simplify use of bytes.Compare\", edit.ReplaceWithPattern(pass.Fset, node, checkBytesCompareRn, m.State))\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"unexpected token %v\", tok))\n\t\t}\n\t\treport.Report(pass, node, fmt.Sprintf(\"should use %sbytes.Equal(%s) instead\", prefix, args), report.FilterGenerated(), report.Fixes(fix))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1004/s1004_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1004\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1004/testdata/go1.0/CheckBytesCompare/compare.go",
    "content": "package pkg\n\nimport \"bytes\"\n\nfunc fn() {\n\t_ = bytes.Compare(nil, nil) == 0 //@ diag(` bytes.Equal`)\n\t_ = bytes.Compare(nil, nil) != 0 //@ diag(`!bytes.Equal`)\n\t_ = bytes.Compare(nil, nil) > 0\n\t_ = bytes.Compare(nil, nil) < 0\n}\n"
  },
  {
    "path": "simple/s1004/testdata/go1.0/CheckBytesCompare/compare.go.golden",
    "content": "package pkg\n\nimport \"bytes\"\n\nfunc fn() {\n\t_ = bytes.Equal(nil, nil)  //@ diag(` bytes.Equal`)\n\t_ = !bytes.Equal(nil, nil) //@ diag(`!bytes.Equal`)\n\t_ = bytes.Compare(nil, nil) > 0\n\t_ = bytes.Compare(nil, nil) < 0\n}\n"
  },
  {
    "path": "simple/s1005/s1005.go",
    "content": "package s1005\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1005\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Drop unnecessary use of the blank identifier`,\n\t\tText:  `In many cases, assigning to the blank identifier is unnecessary.`,\n\t\tBefore: `\nfor _ = range s {}\nx, _ = someMap[key]\n_ = <-ch`,\n\t\tAfter: `\nfor range s{}\nx = someMap[key]\n<-ch`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckUnnecessaryBlankQ1 = pattern.MustParse(`\n\t\t(AssignStmt\n\t\t\t[_ (Ident \"_\")]\n\t\t\t_\n\t\t\t(Or\n\t\t\t\t(IndexExpr _ _)\n\t\t\t\t(UnaryExpr \"<-\" _))) `)\n\tcheckUnnecessaryBlankQ2 = pattern.MustParse(`\n\t\t(AssignStmt\n\t\t\t(Ident \"_\") _ recv@(UnaryExpr \"<-\" _))`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn1 := func(node ast.Node) {\n\t\tif _, ok := code.Match(pass, checkUnnecessaryBlankQ1, node); ok {\n\t\t\tr := *node.(*ast.AssignStmt)\n\t\t\tr.Lhs = r.Lhs[0:1]\n\t\t\treport.Report(pass, node, \"unnecessary assignment to the blank identifier\",\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.Fixes(edit.Fix(\"Remove assignment to blank identifier\", edit.ReplaceWithNode(pass.Fset, node, &r))))\n\t\t} else if m, ok := code.Match(pass, checkUnnecessaryBlankQ2, node); ok {\n\t\t\treport.Report(pass, node, \"unnecessary assignment to the blank identifier\",\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.Fixes(edit.Fix(\"Simplify channel receive operation\", edit.ReplaceWithNode(pass.Fset, node, m.State[\"recv\"].(ast.Node)))))\n\t\t}\n\t}\n\n\tfn3 := func(node ast.Node) {\n\t\trs := node.(*ast.RangeStmt)\n\n\t\tif _, ok := pass.TypesInfo.TypeOf(rs.X).Underlying().(*types.Signature); ok {\n\t\t\t// iteration variables are not optional with rangefunc\n\t\t\treturn\n\t\t}\n\n\t\t// for _\n\t\tif rs.Value == nil && astutil.IsBlank(rs.Key) {\n\t\t\treport.Report(pass, rs.Key, \"unnecessary assignment to the blank identifier\",\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.MinimumLanguageVersion(\"go1.4\"),\n\t\t\t\treport.Fixes(edit.Fix(\"Remove assignment to blank identifier\", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1}))))\n\t\t}\n\n\t\t// for _, _\n\t\tif astutil.IsBlank(rs.Key) && astutil.IsBlank(rs.Value) {\n\t\t\t// FIXME we should mark both key and value\n\t\t\treport.Report(pass, rs.Key, \"unnecessary assignment to the blank identifier\",\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.MinimumLanguageVersion(\"go1.4\"),\n\t\t\t\treport.Fixes(edit.Fix(\"Remove assignment to blank identifier\", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1}))))\n\t\t}\n\n\t\t// for x, _\n\t\tif !astutil.IsBlank(rs.Key) && astutil.IsBlank(rs.Value) {\n\t\t\treport.Report(pass, rs.Value, \"unnecessary assignment to the blank identifier\",\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.MinimumLanguageVersion(\"go1.4\"),\n\t\t\t\treport.Fixes(edit.Fix(\"Remove assignment to blank identifier\", edit.Delete(edit.Range{rs.Key.End(), rs.Value.End()}))))\n\t\t}\n\t}\n\n\tcode.Preorder(pass, fn1, (*ast.AssignStmt)(nil))\n\tcode.Preorder(pass, fn3, (*ast.RangeStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1005/s1005_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1005\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1005/testdata/go1.0/CheckUnnecessaryBlank/LintBlankOK.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar m map[int]int\n\tvar ch chan int\n\tvar fn func() (int, bool)\n\n\tx, _ := m[0] //@ diag(`unnecessary assignment to the blank identifier`)\n\tx, _ = <-ch  //@ diag(`unnecessary assignment to the blank identifier`)\n\tx, _ = fn()\n\t_ = x\n}\n"
  },
  {
    "path": "simple/s1005/testdata/go1.0/CheckUnnecessaryBlank/LintBlankOK.go.golden",
    "content": "package pkg\n\nfunc fn() {\n\tvar m map[int]int\n\tvar ch chan int\n\tvar fn func() (int, bool)\n\n\tx := m[0] //@ diag(`unnecessary assignment to the blank identifier`)\n\tx = <-ch  //@ diag(`unnecessary assignment to the blank identifier`)\n\tx, _ = fn()\n\t_ = x\n}\n"
  },
  {
    "path": "simple/s1005/testdata/go1.0/CheckUnnecessaryBlank/receive-blank.go",
    "content": "package pkg\n\nfunc fn2() {\n\tvar ch chan int\n\t<-ch\n\t_ = <-ch //@ diag(`unnecessary assignment to the blank identifier`)\n\tselect {\n\tcase <-ch:\n\tcase _ = <-ch: //@ diag(`unnecessary assignment to the blank identifier`)\n\t}\n\tx := <-ch\n\ty, _ := <-ch, <-ch\n\t_, z := <-ch, <-ch\n\t_, _, _ = x, y, z\n}\n"
  },
  {
    "path": "simple/s1005/testdata/go1.0/CheckUnnecessaryBlank/receive-blank.go.golden",
    "content": "package pkg\n\nfunc fn2() {\n\tvar ch chan int\n\t<-ch\n\t<-ch //@ diag(`unnecessary assignment to the blank identifier`)\n\tselect {\n\tcase <-ch:\n\tcase <-ch: //@ diag(`unnecessary assignment to the blank identifier`)\n\t}\n\tx := <-ch\n\ty, _ := <-ch, <-ch\n\t_, z := <-ch, <-ch\n\t_, _, _ = x, y, z\n}\n"
  },
  {
    "path": "simple/s1005/testdata/go1.3/CheckUnnecessaryBlank/range.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar m map[string]int\n\n\t// with :=\n\tfor x, _ := range m {\n\t\t_ = x\n\t}\n\t// with =\n\tvar y string\n\t_ = y\n\tfor y, _ = range m {\n\t}\n\n\t// all OK:\n\tfor x := range m {\n\t\t_ = x\n\t}\n\tfor x, y := range m {\n\t\t_, _ = x, y\n\t}\n\tfor _, y := range m {\n\t\t_ = y\n\t}\n\tvar x int\n\t_ = x\n\tfor y = range m {\n\t}\n\tfor y, x = range m {\n\t}\n\tfor _, x = range m {\n\t}\n}\n"
  },
  {
    "path": "simple/s1005/testdata/go1.4/CheckUnnecessaryBlank/range.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar m map[string]int\n\n\t// with :=\n\tfor x, _ := range m { //@ diag(`unnecessary assignment to the blank identifier`)\n\t\t_ = x\n\t}\n\t// with =\n\tvar y string\n\t_ = y\n\tfor y, _ = range m { //@ diag(`unnecessary assignment to the blank identifier`)\n\t}\n\n\tfor _ = range m { //@ diag(`unnecessary assignment to the blank identifier`)\n\t}\n\n\tfor _, _ = range m { //@ diag(`unnecessary assignment to the blank identifier`)\n\t}\n\n\t// all OK:\n\tfor x := range m {\n\t\t_ = x\n\t}\n\tfor x, y := range m {\n\t\t_, _ = x, y\n\t}\n\tfor _, y := range m {\n\t\t_ = y\n\t}\n\tvar x int\n\t_ = x\n\tfor y = range m {\n\t}\n\tfor y, x = range m {\n\t}\n\tfor _, x = range m {\n\t}\n}\n"
  },
  {
    "path": "simple/s1005/testdata/go1.4/CheckUnnecessaryBlank/range.go.golden",
    "content": "package pkg\n\nfunc fn() {\n\tvar m map[string]int\n\n\t// with :=\n\tfor x := range m { //@ diag(`unnecessary assignment to the blank identifier`)\n\t\t_ = x\n\t}\n\t// with =\n\tvar y string\n\t_ = y\n\tfor y = range m { //@ diag(`unnecessary assignment to the blank identifier`)\n\t}\n\n\tfor range m { //@ diag(`unnecessary assignment to the blank identifier`)\n\t}\n\n\tfor range m { //@ diag(`unnecessary assignment to the blank identifier`)\n\t}\n\n\t// all OK:\n\tfor x := range m {\n\t\t_ = x\n\t}\n\tfor x, y := range m {\n\t\t_, _ = x, y\n\t}\n\tfor _, y := range m {\n\t\t_ = y\n\t}\n\tvar x int\n\t_ = x\n\tfor y = range m {\n\t}\n\tfor y, x = range m {\n\t}\n\tfor _, x = range m {\n\t}\n}\n"
  },
  {
    "path": "simple/s1006/s1006.go",
    "content": "package s1006\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1006\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:   `Use \\\"for { ... }\\\" for infinite loops`,\n\t\tText:    `For infinite loops, using \\'for { ... }\\' is the most idiomatic choice.`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tloop := node.(*ast.ForStmt)\n\t\tif loop.Init != nil || loop.Post != nil {\n\t\t\treturn\n\t\t}\n\t\tif !code.IsBoolConst(pass, loop.Cond) || !code.BoolConst(pass, loop.Cond) {\n\t\t\treturn\n\t\t}\n\t\treport.Report(pass, loop, \"should use for {} instead of for true {}\",\n\t\t\treport.ShortRange(),\n\t\t\treport.FilterGenerated())\n\t}\n\tcode.Preorder(pass, fn, (*ast.ForStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1006/s1006_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1006\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1006/testdata/go1.0/CheckForTrue/for-true.go",
    "content": "package pkg\n\nfunc fn() {\n\tfor false {\n\t}\n\tfor true { //@ diag(`should use for`)\n\t}\n\tfor {\n\t}\n\tfor i := 0; true; i++ {\n\t}\n}\n"
  },
  {
    "path": "simple/s1006/testdata/go1.0/CheckForTrue/generated.go",
    "content": "// Code generated by a clever monkey. DO NOT EDIT.\n\npackage pkg\n\nfunc fn3() {\n\tfor true {\n\t}\n}\n\n//line input.go:2\nfunc fn2() {\n\tfor true {\n\t}\n}\n"
  },
  {
    "path": "simple/s1006/testdata/go1.0/CheckForTrue/input.go",
    "content": "package pkg\n\n//@ diag(`should use for {}`)\n\n// the error is produced by generated.go, which pretends that its\n// broken code came from this file.\n"
  },
  {
    "path": "simple/s1007/s1007.go",
    "content": "package s1007\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1007\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Simplify regular expression by using raw string literal`,\n\t\tText: `Raw string literals use backticks instead of quotation marks and do not support\nany escape sequences. This means that the backslash can be used\nfreely, without the need of escaping.\n\nSince regular expressions have their own escape sequences, raw strings\ncan improve their readability.`,\n\t\tBefore:  `regexp.Compile(\"\\\\A(\\\\w+) profile: total \\\\d+\\\\n\\\\z\")`,\n\t\tAfter:   \"regexp.Compile(`\\\\A(\\\\w+) profile: total \\\\d+\\\\n\\\\z`)\",\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\n// TODO(dominikh): support string concat, maybe support constants\nvar query = pattern.MustParse(`(CallExpr (Symbol fn@(Or \"regexp.MustCompile\" \"regexp.Compile\")) [lit@(BasicLit \"STRING\" _)])`)\n\nfunc run(pass *analysis.Pass) (any, error) {\nouter:\n\tfor _, m := range code.Matches(pass, query) {\n\t\tlit := m.State[\"lit\"].(*ast.BasicLit)\n\t\tval := lit.Value\n\t\tif lit.Value[0] != '\"' {\n\t\t\t// already a raw string\n\t\t\tcontinue\n\t\t}\n\t\tif !strings.Contains(val, `\\\\`) {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.Contains(val, \"`\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tbs := false\n\t\tfor _, c := range val {\n\t\t\tif !bs && c == '\\\\' {\n\t\t\t\tbs = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif bs && c == '\\\\' {\n\t\t\t\tbs = false\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif bs {\n\t\t\t\t// backslash followed by non-backslash -> escape sequence\n\t\t\t\tcontinue outer\n\t\t\t}\n\t\t}\n\n\t\treport.Report(pass, lit, fmt.Sprintf(\"should use raw string (`...`) with %s to avoid having to escape twice\", m.State[\"fn\"]), report.FilterGenerated())\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1007/s1007_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1007\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1007/testdata/go1.0/CheckRegexpRaw/regexp-raw.go",
    "content": "package pkg\n\nimport \"regexp\"\n\nfunc fn2() string { return \"\" }\n\nfunc fn() {\n\tx := \"abc\"\n\tconst y = \"abc\"\n\tregexp.MustCompile(`\\\\.`)\n\tregexp.MustCompile(\"\\\\.\") //@ diag(re`should use raw string.+\\.MustCompile`)\n\tregexp.Compile(\"\\\\.\")     //@ diag(re`should use raw string.+\\.Compile`)\n\tregexp.Compile(\"\\\\.`\")\n\tregexp.MustCompile(\"(?m:^lease (.+?) {\\n((?s).+?)\\\\n}\\n)\")\n\tregexp.MustCompile(\"\\\\*/[ \\t\\n\\r\\f\\v]*;\")\n\tregexp.MustCompile(fn2())\n\tregexp.MustCompile(x)\n\tregexp.MustCompile(y)\n}\n"
  },
  {
    "path": "simple/s1008/s1008.go",
    "content": "package s1008\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/token\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1008\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Simplify returning boolean expression`,\n\t\tBefore: `\nif <expr> {\n    return true\n}\nreturn false`,\n\t\tAfter:   `return <expr>`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckIfReturnQIf  = pattern.MustParse(`(IfStmt nil cond [(ReturnStmt [ret@(Builtin (Or \"true\" \"false\"))])] nil)`)\n\tcheckIfReturnQRet = pattern.MustParse(`(ReturnStmt [ret@(Builtin (Or \"true\" \"false\"))])`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tvar cm ast.CommentMap\n\tfn := func(node ast.Node) {\n\t\tif f, ok := node.(*ast.File); ok {\n\t\t\tcm = ast.NewCommentMap(pass.Fset, f, f.Comments)\n\t\t\treturn\n\t\t}\n\n\t\tblock := node.(*ast.BlockStmt)\n\t\tl := len(block.List)\n\t\tif l < 2 {\n\t\t\treturn\n\t\t}\n\t\tn1, n2 := block.List[l-2], block.List[l-1]\n\n\t\tif len(block.List) >= 3 {\n\t\t\tif _, ok := block.List[l-3].(*ast.IfStmt); ok {\n\t\t\t\t// Do not flag a series of if statements\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tm1, ok := code.Match(pass, checkIfReturnQIf, n1)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tm2, ok := code.Match(pass, checkIfReturnQRet, n2)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tif op, ok := m1.State[\"cond\"].(*ast.BinaryExpr); ok {\n\t\t\tswitch op.Op {\n\t\t\tcase token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ:\n\t\t\tdefault:\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tret1 := m1.State[\"ret\"].(*ast.Ident)\n\t\tret2 := m2.State[\"ret\"].(*ast.Ident)\n\t\tif ret1.Name == ret2.Name {\n\t\t\t// we want the function to return true and false, not the\n\t\t\t// same value both times.\n\t\t\treturn\n\t\t}\n\n\t\thasComments := func(n ast.Node) bool {\n\t\t\tcmf := cm.Filter(n)\n\t\t\tfor _, groups := range cmf {\n\t\t\t\tfor _, group := range groups {\n\t\t\t\t\tfor _, cmt := range group.List {\n\t\t\t\t\t\tif strings.HasPrefix(cmt.Text, \"//@ diag\") {\n\t\t\t\t\t\t\t// Staticcheck test cases use comments to mark\n\t\t\t\t\t\t\t// expected diagnostics. Ignore these comments so we\n\t\t\t\t\t\t\t// can test this check.\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\n\t\t// Don't flag if either branch is commented\n\t\tif hasComments(n1) || hasComments(n2) {\n\t\t\treturn\n\t\t}\n\n\t\tcond := m1.State[\"cond\"].(ast.Expr)\n\t\torigCond := cond\n\t\tif ret1.Name == \"false\" {\n\t\t\tcond = negate(pass, cond)\n\t\t}\n\t\treport.Report(pass, n1,\n\t\t\tfmt.Sprintf(\"should use 'return %s' instead of 'if %s { return %s }; return %s'\",\n\t\t\t\treport.Render(pass, cond),\n\t\t\t\treport.Render(pass, origCond), report.Render(pass, ret1), report.Render(pass, ret2)),\n\t\t\treport.FilterGenerated())\n\t}\n\tcode.Preorder(pass, fn, (*ast.File)(nil), (*ast.BlockStmt)(nil))\n\treturn nil, nil\n}\n\nfunc negate(pass *analysis.Pass, expr ast.Expr) ast.Expr {\n\tswitch expr := expr.(type) {\n\tcase *ast.BinaryExpr:\n\t\tout := *expr\n\t\tswitch expr.Op {\n\t\tcase token.EQL:\n\t\t\tout.Op = token.NEQ\n\t\tcase token.LSS:\n\t\t\tout.Op = token.GEQ\n\t\tcase token.GTR:\n\t\t\t// Some builtins never return negative ints; \"len(x) <= 0\" should be \"len(x) == 0\".\n\t\t\tif call, ok := expr.X.(*ast.CallExpr); ok &&\n\t\t\t\tcode.IsCallToAny(pass, call, \"len\", \"cap\", \"copy\") &&\n\t\t\t\tcode.IsIntegerLiteral(pass, expr.Y, constant.MakeInt64(0)) {\n\t\t\t\tout.Op = token.EQL\n\t\t\t} else {\n\t\t\t\tout.Op = token.LEQ\n\t\t\t}\n\t\tcase token.NEQ:\n\t\t\tout.Op = token.EQL\n\t\tcase token.LEQ:\n\t\t\tout.Op = token.GTR\n\t\tcase token.GEQ:\n\t\t\tout.Op = token.LSS\n\t\t}\n\t\treturn &out\n\tcase *ast.Ident, *ast.CallExpr, *ast.IndexExpr, *ast.StarExpr:\n\t\treturn &ast.UnaryExpr{\n\t\t\tOp: token.NOT,\n\t\t\tX:  expr,\n\t\t}\n\tcase *ast.UnaryExpr:\n\t\tif expr.Op == token.NOT {\n\t\t\treturn expr.X\n\t\t}\n\t\treturn &ast.UnaryExpr{\n\t\t\tOp: token.NOT,\n\t\t\tX:  expr,\n\t\t}\n\tdefault:\n\t\treturn &ast.UnaryExpr{\n\t\t\tOp: token.NOT,\n\t\t\tX: &ast.ParenExpr{\n\t\t\t\tX: expr,\n\t\t\t},\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "simple/s1008/s1008_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1008\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1008/testdata/go1.0/CheckIfReturn/comment.go",
    "content": "package pkg\n\nfunc cmt1(x string) bool {\n\t// A\n\tif len(x) > 0 {\n\t\treturn false\n\t}\n\t// B\n\treturn true\n}\n\nfunc cmt2(x string) bool {\n\tif len(x) > 0 { // A\n\t\treturn false\n\t}\n\treturn true // B\n}\n\nfunc cmt3(x string) bool {\n\tif len(x) > 0 {\n\t\treturn false // A\n\t}\n\treturn true // B\n}\n\nfunc cmt4(x string) bool {\n\tif len(x) > 0 {\n\t\treturn false // A\n\t}\n\treturn true\n\t// B\n}\n\nfunc cmt5(x string) bool {\n\tif len(x) > 0 {\n\t\treturn false\n\t}\n\treturn true // A\n}\n\nfunc cmt6(x string) bool {\n\tif len(x) > 0 {\n\t\treturn false // A\n\t}\n\treturn true\n}\n\nfunc cmt7(x string) bool {\n\tif len(x) > 0 {\n\t\t// A\n\t\treturn false\n\t}\n\t// B\n\treturn true\n}\n"
  },
  {
    "path": "simple/s1008/testdata/go1.0/CheckIfReturn/if-return.go",
    "content": "package pkg\n\nfunc fn() bool { return true }\nfunc fn1() bool {\n\tx := true\n\tif x { //@ diag(`should use 'return x'`)\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc fn2() bool {\n\tx := true\n\tif !x {\n\t\treturn true\n\t}\n\tif x {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc fn3() int {\n\tvar x bool\n\tif x {\n\t\treturn 1\n\t}\n\treturn 2\n}\n\nfunc fn4() bool { return true }\n\nfunc fn5() bool {\n\tif fn() { //@ diag(`should use 'return !fn()'`)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc fn6() bool {\n\tif fn3() != fn3() { //@ diag(`should use 'return fn3() != fn3()'`)\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc fn7() bool {\n\tif 1 > 2 { //@ diag(`should use 'return 1 > 2'`)\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc fn8() bool {\n\tif fn() || fn() {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc fn9(x int) bool {\n\tif x > 0 {\n\t\treturn true\n\t}\n\treturn true\n}\n\nfunc fn10(x int) bool {\n\tif x > 0 { //@ diag(`should use 'return x <= 0'`)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc fn11(x bool) bool {\n\tif x { //@ diag(`should use 'return !x'`)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc fn12() bool {\n\tvar x []bool\n\tif x[0] { //@ diag(`should use 'return !x[0]'`)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc fn13(a, b int) bool {\n\tif a != b { //@ diag(`should use 'return a == b' instead of 'if a != b`)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc fn14(a, b int) bool {\n\tif a >= b { //@ diag(`should use 'return a < b' instead of 'if a >= b`)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc fn15() bool {\n\tif !fn() { //@ diag(`should use 'return fn()'`)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc fn16() <-chan bool {\n\tx := make(chan bool, 1)\n\tx <- true\n\treturn x\n}\n\nfunc fn17() bool {\n\tif <-fn16() { //@ diag(`should use 'return !<-fn16()'`)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc fn18() *bool {\n\tx := true\n\treturn &x\n}\n\nfunc fn19() bool {\n\tif *fn18() { //@ diag(`should use 'return !*fn18()'`)\n\t\treturn false\n\t}\n\treturn true\n}\n\nconst a = true\nconst b = false\n\nfunc fn20(x bool) bool {\n\t// Don't match on constants other than the predeclared true and false. This protects us both from build tag woes,\n\t// and from code that breaks when the constant values change.\n\tif x {\n\t\treturn a\n\t}\n\treturn b\n}\n\nfunc fn21(x bool) bool {\n\t// Don't flag, 'true' isn't the predeclared identifier.\n\tconst true = false\n\tif x {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc fn22(x string) bool {\n\tif len(x) > 0 { //@ diag(`should use 'return len(x) == 0'`)\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "simple/s1009/s1009.go",
    "content": "package s1009\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1009\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Omit redundant nil check on slices, maps, and channels`,\n\t\tText: `The \\'len\\' function is defined for all slices, maps, and\nchannels, even nil ones, which have a length of zero. It is not necessary to\ncheck for nil before checking that their length is not zero.`,\n\t\tBefore:  `if x != nil && len(x) != 0 {}`,\n\t\tAfter:   `if len(x) != 0 {}`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar query = pattern.MustParse(`\n\t(BinaryExpr\n\t\t(BinaryExpr\n\t\t\tx\n\t\t\tlhsOp@(Or \"==\" \"!=\")\n\t\t\tnilly)\n\t\touterOp@(Or \"&&\" \"||\")\n\t\t(BinaryExpr\n\t\t\t(CallExpr (Builtin \"len\") [x])\n\t\t\trhsOp\n\t\t\tk))`)\n\n// run checks for the following redundant nil-checks:\n//\n//\tif x == nil || len(x) == 0 {}\n//\tif x == nil || len(x) < N {} (where N != 0)\n//\tif x == nil || len(x) <= N {}\n//\tif x != nil && len(x) != 0 {}\n//\tif x != nil && len(x) == N {} (where N != 0)\n//\tif x != nil && len(x) > N {}\n//\tif x != nil && len(x) >= N {} (where N != 0)\nfunc run(pass *analysis.Pass) (any, error) {\n\tisConstZero := func(expr ast.Expr) (isConst bool, isZero bool) {\n\t\t_, ok := expr.(*ast.BasicLit)\n\t\tif ok {\n\t\t\treturn true, code.IsIntegerLiteral(pass, expr, constant.MakeInt64(0))\n\t\t}\n\t\tid, ok := expr.(*ast.Ident)\n\t\tif !ok {\n\t\t\treturn false, false\n\t\t}\n\t\tc, ok := pass.TypesInfo.ObjectOf(id).(*types.Const)\n\t\tif !ok {\n\t\t\treturn false, false\n\t\t}\n\t\treturn true, c.Val().Kind() == constant.Int && c.Val().String() == \"0\"\n\t}\n\n\tfor node, m := range code.Matches(pass, query) {\n\t\tx := m.State[\"x\"].(ast.Expr)\n\t\touterOp := m.State[\"outerOp\"].(token.Token)\n\t\tlhsOp := m.State[\"lhsOp\"].(token.Token)\n\t\trhsOp := m.State[\"rhsOp\"].(token.Token)\n\t\tnilly := m.State[\"nilly\"].(ast.Expr)\n\t\tk := m.State[\"k\"].(ast.Expr)\n\t\teqNil := outerOp == token.LOR\n\n\t\tif code.MayHaveSideEffects(pass, x, nil) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif eqNil && lhsOp != token.EQL {\n\t\t\tcontinue\n\t\t}\n\t\tif !eqNil && lhsOp != token.NEQ {\n\t\t\tcontinue\n\t\t}\n\t\tif !code.IsNil(pass, nilly) {\n\t\t\tcontinue\n\t\t}\n\t\tisConst, isZero := isConstZero(k)\n\t\tif !isConst {\n\t\t\tcontinue\n\t\t}\n\n\t\tif eqNil {\n\t\t\tswitch rhsOp {\n\t\t\tcase token.EQL:\n\t\t\t\t// avoid false positive for \"xx == nil || len(xx) == <non-zero>\"\n\t\t\t\tif !isZero {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\tcase token.LEQ:\n\t\t\t\t// ok\n\t\t\tcase token.LSS:\n\t\t\t\t// avoid false positive for \"xx == nil || len(xx) < 0\"\n\t\t\t\tif isZero {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tcontinue\n\t\t\t}\n\t\t} else {\n\t\t\tswitch rhsOp {\n\t\t\tcase token.EQL:\n\t\t\t\t// avoid false positive for \"xx != nil && len(xx) == 0\"\n\t\t\t\tif isZero {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\tcase token.GEQ:\n\t\t\t\t// avoid false positive for \"xx != nil && len(xx) >= 0\"\n\t\t\t\tif isZero {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\tcase token.NEQ:\n\t\t\t\t// avoid false positive for \"xx != nil && len(xx) != <non-zero>\"\n\t\t\t\tif !isZero {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\tcase token.GTR:\n\t\t\t\t// ok\n\t\t\tdefault:\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\t// finally check that xx type is one of array, slice, map or chan\n\t\t// this is to prevent false positive in case if xx is a pointer to an array\n\t\ttyp := pass.TypesInfo.TypeOf(x)\n\t\tvar nilType string\n\t\tok := typeutil.All(typ, func(term *types.Term) bool {\n\t\t\tswitch term.Type().Underlying().(type) {\n\t\t\tcase *types.Slice:\n\t\t\t\tnilType = \"nil slices\"\n\t\t\t\treturn true\n\t\t\tcase *types.Map:\n\t\t\t\tnilType = \"nil maps\"\n\t\t\t\treturn true\n\t\t\tcase *types.Chan:\n\t\t\t\tnilType = \"nil channels\"\n\t\t\t\treturn true\n\t\t\tcase *types.Pointer:\n\t\t\t\treturn false\n\t\t\tcase *types.TypeParam:\n\t\t\t\treturn false\n\t\t\tdefault:\n\t\t\t\tlint.ExhaustiveTypeSwitch(term.Type().Underlying())\n\t\t\t\treturn false\n\t\t\t}\n\t\t})\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\treport.Report(pass, node,\n\t\t\tfmt.Sprintf(\"should omit nil check; len() for %s is defined as zero\", nilType),\n\t\t\treport.FilterGenerated())\n\t}\n\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1009/s1009_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1009\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1009/testdata/go1.0/CheckRedundantNilCheckWithLen/nil-len.go",
    "content": "package pkg\n\nfunc gen() []int { return make([]int, 1) }\n\nfunc fn() {\n\tvar pa *[5]int\n\tvar s []int\n\tvar m map[int]int\n\tvar ch chan int\n\n\tif s == nil || len(s) == 0 { //@ diag(re`should omit nil check.+for nil slices`)\n\t}\n\tif m == nil || len(m) == 0 { //@ diag(re`should omit nil check.+for nil maps`)\n\t}\n\tif ch == nil || len(ch) == 0 { //@ diag(re`should omit nil check.+for nil channels`)\n\t}\n\n\tif s != nil && len(s) != 0 { //@ diag(`should omit nil check`)\n\t}\n\tif m != nil && len(m) > 0 { //@ diag(`should omit nil check`)\n\t}\n\tif s != nil && len(s) > 5 { //@ diag(`should omit nil check`)\n\t}\n\tif s != nil && len(s) >= 5 { //@ diag(`should omit nil check`)\n\t}\n\tconst five = 5\n\tif s != nil && len(s) == five { //@ diag(`should omit nil check`)\n\t}\n\n\tif ch != nil && len(ch) == 5 { //@ diag(`should omit nil check`)\n\t}\n\n\tif pa == nil || len(pa) == 0 { // nil check cannot be removed with pointer to an array\n\t}\n\tif s == nil || len(m) == 0 { // different variables\n\t}\n\tif s != nil && len(m) == 1 { // different variables\n\t}\n\n\tvar ch2 chan int\n\tif ch == ch2 || len(ch) == 0 { // not comparing with nil\n\t}\n\tif ch != ch2 && len(ch) != 0 { // not comparing with nil\n\t}\n\n\tconst zero = 0\n\tif s != nil && len(s) == zero { // nil check is not redundant here\n\t}\n\tif s != nil && len(s) == 0 { // nil check is not redundant here\n\t}\n\tif s != nil && len(s) >= 0 { // nil check is not redundant here (though len(s) >= 0 is)\n\t}\n\tone := 1\n\tif s != nil && len(s) == one { // nil check is not redundant here\n\t}\n\tif s != nil && len(s) == len(m) { // nil check is not redundant here\n\t}\n\tif s != nil && len(s) != 1 { // nil check is not redundant here\n\t}\n\tif s != nil && len(s) < 5 { // nil check is not redundant here\n\t}\n\tif s != nil && len(s) <= 5 { // nil check is not redundant here\n\t}\n\tif s != nil && len(s) != len(ch) { // nil check is not redundant here\n\t}\n\n\tif gen() != nil && len(gen()) != 0 { // nil check is not redundant, gen() isn't pure\n\t}\n}\n\nfunc fn3() {\n\tvar x []int\n\n\tif x == nil || len(x) == 0 { //@ diag(`should omit nil check`)\n\t}\n\n\tlen := func([]int) int { return 10 }\n\tif x == nil || len(x) == 0 {\n\t}\n}\n\nfunc issue1527() {\n\tvar t struct {\n\t\tpa *[5]int\n\t\ts  []int\n\t\tm  map[uint64]bool\n\t\tch chan int\n\t}\n\n\tif t.s == nil || len(t.s) == 0 { //@ diag(`should omit nil check`)\n\t}\n\tif t.m == nil || len(t.m) == 0 { //@ diag(`should omit nil check`)\n\t}\n\tif t.ch == nil || len(t.ch) == 0 { //@ diag(`should omit nil check`)\n\t}\n\tif t.pa == nil || len(t.pa) == 0 { // nil check cannot be removed with pointer to an array\n\t}\n}\n\nfunc issue1605() {\n\tvar s []int\n\tvar m map[int]int\n\tvar ch chan int\n\n\tif s == nil || len(s) <= 0 { //@ diag(`should omit nil check`)\n\t}\n\tif m == nil || len(m) <= 0 { //@ diag(`should omit nil check`)\n\t}\n\tif ch == nil || len(ch) <= 0 { //@ diag(`should omit nil check`)\n\t}\n\n\tif s == nil || len(s) < 2 { //@ diag(`should omit nil check`)\n\t}\n\tif m == nil || len(m) < 2 { //@ diag(`should omit nil check`)\n\t}\n\tif ch == nil || len(ch) < 2 { //@ diag(`should omit nil check`)\n\t}\n\n\tif s == nil || len(s) <= 2 { //@ diag(`should omit nil check`)\n\t}\n\tif m == nil || len(m) <= 2 { //@ diag(`should omit nil check`)\n\t}\n\tif ch == nil || len(ch) <= 2 { //@ diag(`should omit nil check`)\n\t}\n\n\tif s == nil || len(s) < 0 { // nil check is not redundant here (len(s) < 0 is impossible)\n\t}\n\tif m == nil || len(m) < 0 { // nil check is not redundant here (len(m) < 0 is impossible)\n\t}\n\tif ch == nil || len(ch) < 0 { // nil check is not redundant here (len(ch) < 0 is impossible)\n\t}\n\n\tif s == nil || len(s) > 2 { // nil check is not redundant here\n\t}\n\tif m == nil || len(m) > 2 { // nil check is not redundant here\n\t}\n\tif ch == nil || len(ch) > 2 { // nil check is not redundant here\n\t}\n}\n"
  },
  {
    "path": "simple/s1009/testdata/go1.18/CheckRedundantNilCheckWithLen/nil-len_generics.go",
    "content": "package pkg\n\nfunc fn1[T []int | *[4]int](a T) {\n\tif a != nil && len(a) > 0 { // don't flag, because of the pointer\n\t}\n}\n\nfunc fn2[T []int | []string | map[string]int](a T) {\n\tif a != nil && len(a) > 0 { //@ diag(`should omit nil check`)\n\t}\n}\n"
  },
  {
    "path": "simple/s1010/s1010.go",
    "content": "package s1010\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1010\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Omit default slice index`,\n\t\tText: `When slicing, the second index defaults to the length of the value,\nmaking \\'s[n:len(s)]\\' and \\'s[n:]\\' equivalent.`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkSlicingQ = pattern.MustParse(`(SliceExpr x@(Object _) low (CallExpr (Builtin \"len\") [x]) nil)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node := range code.Matches(pass, checkSlicingQ) {\n\t\texpr := node.(*ast.SliceExpr)\n\t\treport.Report(pass, expr.High,\n\t\t\t\"should omit second index in slice, s[a:len(s)] is identical to s[a:]\",\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(\"Simplify slice expression\", edit.Delete(expr.High))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1010/s1010_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1010\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1010/testdata/go1.0/CheckSlicing/slicing.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar s [5]int\n\t_ = s[:len(s)] //@ diag(`omit second index`)\n\n\tlen := func(s [5]int) int { return -1 }\n\t_ = s[:len(s)]\n}\n"
  },
  {
    "path": "simple/s1010/testdata/go1.0/CheckSlicing/slicing.go.golden",
    "content": "package pkg\n\nfunc fn() {\n\tvar s [5]int\n\t_ = s[:] //@ diag(`omit second index`)\n\n\tlen := func(s [5]int) int { return -1 }\n\t_ = s[:len(s)]\n}\n"
  },
  {
    "path": "simple/s1011/s1011.go",
    "content": "package s1011\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/facts/purity\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1011\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer, purity.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Use a single \\'append\\' to concatenate two slices`,\n\t\tBefore: `\nfor _, e := range y {\n    x = append(x, e)\n}\n\nfor i := range y {\n    x = append(x, y[i])\n}\n\nfor i := range y {\n    v := y[i]\n    x = append(x, v)\n}`,\n\n\t\tAfter: `\nx = append(x, y...)\nx = append(x, y...)\nx = append(x, y...)`,\n\t\tSince: \"2017.1\",\n\t\t// MergeIfAll because y might not be a slice under all build tags.\n\t\tMergeIf: lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkLoopAppendQ = pattern.MustParse(`\n(Or\n\t(RangeStmt\n\t\t(Ident \"_\")\n\t\tval@(Object _)\n\t\t_\n\t\tx\n\t\t[(AssignStmt [lhs] \"=\" [(CallExpr (Builtin \"append\") [lhs val])])])\n\t(RangeStmt\n\t\tidx@(Object _)\n\t\tnil\n\t\t_\n\t\tx\n\t\t[(AssignStmt [lhs] \"=\" [(CallExpr (Builtin \"append\") [lhs (IndexExpr x idx)])])])\n\t(RangeStmt\n\t\tidx@(Object _)\n\t\tnil\n\t\t_\n\t\tx\n\t\t[(AssignStmt val@(Object _) \":=\" (IndexExpr x idx))\n\t\t(AssignStmt [lhs] \"=\" [(CallExpr (Builtin \"append\") [lhs val])])]))`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tpure := pass.ResultOf[purity.Analyzer].(purity.Result)\n\n\tfor node, m := range code.Matches(pass, checkLoopAppendQ) {\n\t\tif val, ok := m.State[\"val\"].(types.Object); ok && code.RefersTo(pass, m.State[\"lhs\"].(ast.Expr), val) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif m.State[\"idx\"] != nil && code.MayHaveSideEffects(pass, m.State[\"x\"].(ast.Expr), pure) {\n\t\t\t// When using an index-based loop, x gets evaluated repeatedly and thus should be pure.\n\t\t\t// This doesn't matter for value-based loops, because x only gets evaluated once.\n\t\t\tcontinue\n\t\t}\n\n\t\tif idx, ok := m.State[\"idx\"].(types.Object); ok && code.RefersTo(pass, m.State[\"lhs\"].(ast.Expr), idx) {\n\t\t\t// The lhs mustn't refer to the index loop variable.\n\t\t\tcontinue\n\t\t}\n\n\t\tif code.MayHaveSideEffects(pass, m.State[\"lhs\"].(ast.Expr), pure) {\n\t\t\t// The lhs may be dynamic and return different values on each iteration. For example:\n\t\t\t//\n\t\t\t// \tfunc bar() map[int][]int { /* return one of several maps */ }\n\t\t\t//\n\t\t\t// \tfunc foo(x []int, y [][]int) {\n\t\t\t// \t\tfor i := range x {\n\t\t\t// \t\t\tbar()[0] = append(bar()[0], x[i])\n\t\t\t// \t\t}\n\t\t\t// \t}\n\t\t\t//\n\t\t\t// The dynamic nature of the lhs might also affect the value of the index.\n\t\t\tcontinue\n\t\t}\n\n\t\tsrc := pass.TypesInfo.TypeOf(m.State[\"x\"].(ast.Expr))\n\t\tdst := pass.TypesInfo.TypeOf(m.State[\"lhs\"].(ast.Expr))\n\t\tif !types.Identical(src, dst) {\n\t\t\tcontinue\n\t\t}\n\n\t\tr := &ast.AssignStmt{\n\t\t\tLhs: []ast.Expr{m.State[\"lhs\"].(ast.Expr)},\n\t\t\tTok: token.ASSIGN,\n\t\t\tRhs: []ast.Expr{\n\t\t\t\t&ast.CallExpr{\n\t\t\t\t\tFun: &ast.Ident{Name: \"append\"},\n\t\t\t\t\tArgs: []ast.Expr{\n\t\t\t\t\t\tm.State[\"lhs\"].(ast.Expr),\n\t\t\t\t\t\tm.State[\"x\"].(ast.Expr),\n\t\t\t\t\t},\n\t\t\t\t\tEllipsis: 1,\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\treport.Report(pass, node, fmt.Sprintf(\"should replace loop with %s\", report.Render(pass, r)),\n\t\t\treport.ShortRange(),\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(\"Replace loop with call to append\", edit.ReplaceWithNode(pass.Fset, node, r))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1011/s1011_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1011\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1011/testdata/go1.0/CheckLoopAppend/loop-append.go",
    "content": "package pkg\n\ntype T struct {\n\tF string\n}\n\nfunc fn1() {\n\tvar x []interface{}\n\tvar y []int\n\n\tfor _, v := range y {\n\t\tx = append(x, v)\n\t}\n\n\tvar a, b []int\n\tfor _, v := range a { //@ diag(`should replace loop`)\n\t\tb = append(b, v)\n\t}\n\n\tvar a2, b2 []int\n\tfor i := range a2 { //@ diag(`should replace loop`)\n\t\tb2 = append(b2, a2[i])\n\t}\n\n\tvar a3, b3 []int\n\tfor i := range a3 { //@ diag(`should replace loop`)\n\t\tv := a3[i]\n\t\tb3 = append(b3, v)\n\t}\n\n\tvar a4 []int\n\tfor i := range fn6() {\n\t\ta4 = append(a4, fn6()[i])\n\t}\n\n\tvar m map[string]int\n\tvar c []int\n\tfor _, v := range m {\n\t\tc = append(c, v)\n\t}\n\n\tvar t []T\n\tvar m2 map[string][]T\n\n\tfor _, tt := range t {\n\t\tm2[tt.F] = append(m2[tt.F], tt)\n\t}\n\n\tvar out []T\n\tfor _, tt := range t {\n\t\tout = append(m2[tt.F], tt)\n\t}\n\t_ = out\n}\n\nfunc fn2() {\n\tvar v struct {\n\t\tV int\n\t}\n\tvar in []int\n\tvar out []int\n\n\tfor _, v.V = range in {\n\t\tout = append(out, v.V)\n\t}\n}\n\nfunc fn3() {\n\tvar t []T\n\tvar out [][]T\n\tvar m2 map[string][]T\n\n\tfor _, tt := range t {\n\t\tout = append(out, m2[tt.F])\n\t}\n}\n\nfunc fn4() {\n\tvar a, b, c []int\n\tfor _, v := range a {\n\t\tb = append(c, v)\n\t}\n\t_ = b\n}\n\nfunc fn5() {\n\tvar t []T\n\tvar m2 map[string][]T\n\tvar out []T\n\tfor _, tt := range t {\n\t\tout = append(m2[tt.F], tt)\n\t}\n\t_ = out\n}\n\nfunc fn6() []int {\n\treturn []int{1, 2, 3}\n}\n\nfunc fn7() {\n\tvar x []int\n\tfor _, v := range fn6() { //@ diag(`should replace loop`)\n\t\t// Purity doesn't matter here\n\t\tx = append(x, v)\n\t}\n\n\tfor i := range fn6() {\n\t\t// Purity does matter here\n\t\tx = append(x, fn6()[i])\n\t}\n}\n\nfunc fn8() {\n\t// The lhs isn't allowed to refer to i\n\tvar i int\n\tvar x []int\n\tvar y [][]int\n\tfor i = range x {\n\t\ty[i] = append(y[i], x[i])\n\t}\n\tfor i := range x {\n\t\ty[i] = append(y[i], x[i])\n\t}\n}\n\nfunc fn9() {\n\t// The lhs isn't allowed to have side effects\n\tbar := func() map[int][]int { return nil }\n\tvar x []int\n\tfor i := range x {\n\t\tbar()[0] = append(bar()[0], x[i])\n\t}\n}\n"
  },
  {
    "path": "simple/s1011/testdata/go1.0/CheckLoopAppend/loop-append.go.golden",
    "content": "package pkg\n\ntype T struct {\n\tF string\n}\n\nfunc fn1() {\n\tvar x []interface{}\n\tvar y []int\n\n\tfor _, v := range y {\n\t\tx = append(x, v)\n\t}\n\n\tvar a, b []int\n\tb = append(b, a...)\n\n\tvar a2, b2 []int\n\tb2 = append(b2, a2...)\n\n\tvar a3, b3 []int\n\tb3 = append(b3, a3...)\n\n\tvar a4 []int\n\tfor i := range fn6() {\n\t\ta4 = append(a4, fn6()[i])\n\t}\n\n\tvar m map[string]int\n\tvar c []int\n\tfor _, v := range m {\n\t\tc = append(c, v)\n\t}\n\n\tvar t []T\n\tvar m2 map[string][]T\n\n\tfor _, tt := range t {\n\t\tm2[tt.F] = append(m2[tt.F], tt)\n\t}\n\n\tvar out []T\n\tfor _, tt := range t {\n\t\tout = append(m2[tt.F], tt)\n\t}\n\t_ = out\n}\n\nfunc fn2() {\n\tvar v struct {\n\t\tV int\n\t}\n\tvar in []int\n\tvar out []int\n\n\tfor _, v.V = range in {\n\t\tout = append(out, v.V)\n\t}\n}\n\nfunc fn3() {\n\tvar t []T\n\tvar out [][]T\n\tvar m2 map[string][]T\n\n\tfor _, tt := range t {\n\t\tout = append(out, m2[tt.F])\n\t}\n}\n\nfunc fn4() {\n\tvar a, b, c []int\n\tfor _, v := range a {\n\t\tb = append(c, v)\n\t}\n\t_ = b\n}\n\nfunc fn5() {\n\tvar t []T\n\tvar m2 map[string][]T\n\tvar out []T\n\tfor _, tt := range t {\n\t\tout = append(m2[tt.F], tt)\n\t}\n\t_ = out\n}\n\nfunc fn6() []int {\n\treturn []int{1, 2, 3}\n}\n\nfunc fn7() {\n\tvar x []int\n\tx = append(x, fn6()...)\n\n\tfor i := range fn6() {\n\t\t// Purity does matter here\n\t\tx = append(x, fn6()[i])\n\t}\n}\n\nfunc fn8() {\n\t// The lhs isn't allowed to refer to i\n\tvar i int\n\tvar x []int\n\tvar y [][]int\n\tfor i = range x {\n\t\ty[i] = append(y[i], x[i])\n\t}\n\tfor i := range x {\n\t\ty[i] = append(y[i], x[i])\n\t}\n}\n\nfunc fn9() {\n\t// The lhs isn't allowed to have side effects\n\tbar := func() map[int][]int { return nil }\n\tvar x []int\n\tfor i := range x {\n\t\tbar()[0] = append(bar()[0], x[i])\n\t}\n}\n"
  },
  {
    "path": "simple/s1012/s1012.go",
    "content": "package s1012\n\nimport (\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1012\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Replace \\'time.Now().Sub(x)\\' with \\'time.Since(x)\\'`,\n\t\tText: `The \\'time.Since\\' helper has the same effect as using \\'time.Now().Sub(x)\\'\nbut is easier to read.`,\n\t\tBefore:  `time.Now().Sub(x)`,\n\t\tAfter:   `time.Since(x)`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckTimeSinceQ = pattern.MustParse(`(CallExpr (SelectorExpr (CallExpr (Symbol \"time.Now\") []) (Symbol \"(time.Time).Sub\")) [arg])`)\n\tcheckTimeSinceR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident \"time\") (Ident \"Since\")) [arg])`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkTimeSinceQ) {\n\t\tedits := code.EditMatch(pass, node, m, checkTimeSinceR)\n\t\treport.Report(pass, node, \"should use time.Since instead of time.Now().Sub\",\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(\"Replace with call to time.Since\", edits...)))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1012/s1012_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1012\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1012/testdata/go1.0/CheckTimeSince/time-since.go",
    "content": "package pkg\n\nimport \"time\"\n\nfunc fn() {\n\tt1 := time.Now()\n\t_ = time.Now().Sub(t1) //@ diag(`time.Since`)\n\t_ = time.Date(0, 0, 0, 0, 0, 0, 0, nil).Sub(t1)\n}\n"
  },
  {
    "path": "simple/s1012/testdata/go1.0/CheckTimeSince/time-since.go.golden",
    "content": "package pkg\n\nimport \"time\"\n\nfunc fn() {\n\tt1 := time.Now()\n\t_ = time.Since(t1) //@ diag(`time.Since`)\n\t_ = time.Date(0, 0, 0, 0, 0, 0, 0, nil).Sub(t1)\n}\n"
  },
  {
    "path": "simple/s1016/s1016.go",
    "content": "package s1016\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"go/version\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1016\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Use a type conversion instead of manually copying struct fields`,\n\t\tText: `Two struct types with identical fields can be converted between each\nother. In older versions of Go, the fields had to have identical\nstruct tags. Since Go 1.8, however, struct tags are ignored during\nconversions. It is thus not necessary to manually copy every field\nindividually.`,\n\t\tBefore: `\nvar x T1\ny := T2{\n    Field1: x.Field1,\n    Field2: x.Field2,\n}`,\n\t\tAfter: `\nvar x T1\ny := T2(x)`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// TODO(dh): support conversions between type parameters\n\tfn := func(c inspector.Cursor) {\n\t\tnode := c.Node()\n\t\tif unary, ok := c.Parent().Node().(*ast.UnaryExpr); ok && unary.Op == token.AND {\n\t\t\t// Do not suggest type conversion between pointers\n\t\t\treturn\n\t\t}\n\n\t\tlit := node.(*ast.CompositeLit)\n\t\tvar typ1 types.Type\n\t\tvar named1 *types.Named\n\t\tswitch typ := pass.TypesInfo.TypeOf(lit.Type).(type) {\n\t\tcase *types.Named:\n\t\t\ttyp1 = typ\n\t\t\tnamed1 = typ\n\t\tcase *types.Alias:\n\t\t\tua := types.Unalias(typ)\n\t\t\tif n, ok := ua.(*types.Named); ok {\n\t\t\t\ttyp1 = typ\n\t\t\t\tnamed1 = n\n\t\t\t}\n\t\t}\n\t\tif typ1 == nil {\n\t\t\treturn\n\t\t}\n\t\ts1, ok := typ1.Underlying().(*types.Struct)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tvar typ2 types.Type\n\t\tvar named2 *types.Named\n\t\tvar ident *ast.Ident\n\t\tgetSelType := func(expr ast.Expr) (types.Type, *ast.Ident, bool) {\n\t\t\tsel, ok := expr.(*ast.SelectorExpr)\n\t\t\tif !ok {\n\t\t\t\treturn nil, nil, false\n\t\t\t}\n\t\t\tident, ok := sel.X.(*ast.Ident)\n\t\t\tif !ok {\n\t\t\t\treturn nil, nil, false\n\t\t\t}\n\t\t\ttyp := pass.TypesInfo.TypeOf(sel.X)\n\t\t\treturn typ, ident, typ != nil\n\t\t}\n\t\tif len(lit.Elts) == 0 {\n\t\t\treturn\n\t\t}\n\t\tif s1.NumFields() != len(lit.Elts) {\n\t\t\treturn\n\t\t}\n\t\tfor i, elt := range lit.Elts {\n\t\t\tvar t types.Type\n\t\t\tvar id *ast.Ident\n\t\t\tvar ok bool\n\t\t\tswitch elt := elt.(type) {\n\t\t\tcase *ast.SelectorExpr:\n\t\t\t\tt, id, ok = getSelType(elt)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif i >= s1.NumFields() || s1.Field(i).Name() != elt.Sel.Name {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\tcase *ast.KeyValueExpr:\n\t\t\t\tvar sel *ast.SelectorExpr\n\t\t\t\tsel, ok = elt.Value.(*ast.SelectorExpr)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif elt.Key.(*ast.Ident).Name != sel.Sel.Name {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tt, id, ok = getSelType(elt.Value)\n\t\t\t}\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// All fields must be initialized from the same object\n\t\t\tif ident != nil && pass.TypesInfo.ObjectOf(ident) != pass.TypesInfo.ObjectOf(id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tswitch t := t.(type) {\n\t\t\tcase *types.Named:\n\t\t\t\ttyp2 = t\n\t\t\t\tnamed2 = t\n\t\t\tcase *types.Alias:\n\t\t\t\tif n, ok := types.Unalias(t).(*types.Named); ok {\n\t\t\t\t\ttyp2 = t\n\t\t\t\t\tnamed2 = n\n\t\t\t\t}\n\t\t\t}\n\t\t\tif typ2 == nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tident = id\n\t\t}\n\n\t\tif typ2 == nil {\n\t\t\treturn\n\t\t}\n\n\t\tif named1.Obj().Pkg() != named2.Obj().Pkg() {\n\t\t\t// Do not suggest type conversions between different\n\t\t\t// packages. Types in different packages might only match\n\t\t\t// by coincidence. Furthermore, if the dependency ever\n\t\t\t// adds more fields to its type, it could break the code\n\t\t\t// that relies on the type conversion to work.\n\t\t\treturn\n\t\t}\n\n\t\ts2, ok := typ2.Underlying().(*types.Struct)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tif typ1 == typ2 {\n\t\t\treturn\n\t\t}\n\t\tif version.Compare(code.LanguageVersion(pass, node), \"go1.8\") >= 0 {\n\t\t\tif !types.IdenticalIgnoreTags(s1, s2) {\n\t\t\t\treturn\n\t\t\t}\n\t\t} else {\n\t\t\tif !types.Identical(s1, s2) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tr := &ast.CallExpr{\n\t\t\tFun:  lit.Type,\n\t\t\tArgs: []ast.Expr{ident},\n\t\t}\n\t\treport.Report(pass, node,\n\t\t\tfmt.Sprintf(\"should convert %s (type %s) to %s instead of using struct literal\", ident.Name, types.TypeString(typ2, types.RelativeTo(pass.Pkg)), types.TypeString(typ1, types.RelativeTo(pass.Pkg))),\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(\"Use type conversion\", edit.ReplaceWithNode(pass.Fset, node, r))))\n\t}\n\tfor c := range code.Cursor(pass).Preorder((*ast.CompositeLit)(nil)) {\n\t\tfn(c)\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1016/s1016_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1016\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1016/testdata/go1.0/CheckSimplerStructConversion/convert.go",
    "content": "package pkg\n\ntype t1 struct {\n\ta int\n\tb int\n}\n\ntype t2 struct {\n\ta int\n\tb int\n}\n\ntype t3 t1\n\nfunc fn() {\n\tv1 := t1{1, 2}\n\tv2 := t2{1, 2}\n\t_ = t2{v1.a, v1.b}       //@ diag(`should convert v1`)\n\t_ = t2{a: v1.a, b: v1.b} //@ diag(`should convert v1`)\n\t_ = t2{b: v1.b, a: v1.a} //@ diag(`should convert v1`)\n\t_ = t3{v1.a, v1.b}       //@ diag(`should convert v1`)\n\n\t_ = t3{v1.a, v2.b}\n\n\t_ = t2{v1.b, v1.a}\n\t_ = t2{a: v1.b, b: v1.a}\n\t_ = t2{a: v1.a}\n\t_ = t1{v1.a, v1.b}\n\n\tv := t1{1, 2}\n\t_ = &t2{v.a, v.b}\n}\n"
  },
  {
    "path": "simple/s1016/testdata/go1.0/CheckSimplerStructConversion/convert.go.golden",
    "content": "package pkg\n\ntype t1 struct {\n\ta int\n\tb int\n}\n\ntype t2 struct {\n\ta int\n\tb int\n}\n\ntype t3 t1\n\nfunc fn() {\n\tv1 := t1{1, 2}\n\tv2 := t2{1, 2}\n\t_ = t2(v1) //@ diag(`should convert v1`)\n\t_ = t2(v1) //@ diag(`should convert v1`)\n\t_ = t2(v1) //@ diag(`should convert v1`)\n\t_ = t3(v1) //@ diag(`should convert v1`)\n\n\t_ = t3{v1.a, v2.b}\n\n\t_ = t2{v1.b, v1.a}\n\t_ = t2{a: v1.b, b: v1.a}\n\t_ = t2{a: v1.a}\n\t_ = t1{v1.a, v1.b}\n\n\tv := t1{1, 2}\n\t_ = &t2{v.a, v.b}\n}\n"
  },
  {
    "path": "simple/s1016/testdata/go1.18/CheckSimplerStructConversion/convert_generics.go",
    "content": "package pkg\n\ntype T1 struct {\n\tA int\n\tB int\n}\n\ntype T2 struct {\n\tA int\n\tB int\n}\n\ntype T3[T any] struct {\n\tA T\n\tB T\n}\n\ntype T4[T any] struct {\n\tA T\n\tB T\n}\n\nfunc _() {\n\tt1 := T1{0, 0}\n\tt3 := T3[int]{0, 0}\n\n\t_ = T2{t1.A, t1.B} //@ diag(`(type T1) to T2`)\n\t_ = T2{t3.A, t3.B} //@ diag(`(type T3[int]) to T2`)\n\n\t_ = T4[int]{t1.A, t1.B} //@ diag(`(type T1) to T4[int]`)\n\t_ = T4[int]{t3.A, t3.B} //@ diag(`(type T3[int]) to T4[int]`)\n\n\t_ = T4[any]{t3.A, t3.B}\n}\n"
  },
  {
    "path": "simple/s1016/testdata/go1.18/CheckSimplerStructConversion/convert_generics.go.golden",
    "content": "package pkg\n\ntype T1 struct {\n\tA int\n\tB int\n}\n\ntype T2 struct {\n\tA int\n\tB int\n}\n\ntype T3[T any] struct {\n\tA T\n\tB T\n}\n\ntype T4[T any] struct {\n\tA T\n\tB T\n}\n\nfunc _() {\n\tt1 := T1{0, 0}\n\tt3 := T3[int]{0, 0}\n\n\t_ = T2(t1) //@ diag(`(type T1) to T2`)\n\t_ = T2(t3) //@ diag(`(type T3[int]) to T2`)\n\n\t_ = T4[int](t1) //@ diag(`(type T1) to T4[int]`)\n\t_ = T4[int](t3) //@ diag(`(type T3[int]) to T4[int]`)\n\n\t_ = T4[any]{t3.A, t3.B}\n}\n"
  },
  {
    "path": "simple/s1016/testdata/go1.7/CheckSimplerStructConversion/convert.go",
    "content": "package pkg\n\ntype t1 struct {\n\ta int\n\tb int\n}\n\ntype t2 struct {\n\ta int\n\tb int\n}\n\ntype t3 struct {\n\ta int `tag`\n\tb int `tag`\n}\n\nfunc fn() {\n\tv1 := t1{1, 2}\n\t_ = t2{v1.a, v1.b} //@ diag(`should convert v1`)\n\t_ = t3{v1.a, v1.b}\n}\n"
  },
  {
    "path": "simple/s1016/testdata/go1.7/CheckSimplerStructConversion/convert.go.golden",
    "content": "package pkg\n\ntype t1 struct {\n\ta int\n\tb int\n}\n\ntype t2 struct {\n\ta int\n\tb int\n}\n\ntype t3 struct {\n\ta int `tag`\n\tb int `tag`\n}\n\nfunc fn() {\n\tv1 := t1{1, 2}\n\t_ = t2(v1) //@ diag(`should convert v1`)\n\t_ = t3{v1.a, v1.b}\n}\n"
  },
  {
    "path": "simple/s1016/testdata/go1.8/CheckSimplerStructConversion/convert.go",
    "content": "package pkg\n\ntype t1 struct {\n\ta int\n\tb int\n}\n\ntype t2 struct {\n\ta int\n\tb int\n}\n\ntype t3 struct {\n\ta int `tag`\n\tb int `tag`\n}\n\nfunc fn() {\n\tv1 := t1{1, 2}\n\t_ = t2{v1.a, v1.b} //@ diag(`should convert v1`)\n\t_ = t3{v1.a, v1.b} //@ diag(`should convert v1`)\n}\n"
  },
  {
    "path": "simple/s1016/testdata/go1.8/CheckSimplerStructConversion/convert.go.golden",
    "content": "package pkg\n\ntype t1 struct {\n\ta int\n\tb int\n}\n\ntype t2 struct {\n\ta int\n\tb int\n}\n\ntype t3 struct {\n\ta int `tag`\n\tb int `tag`\n}\n\nfunc fn() {\n\tv1 := t1{1, 2}\n\t_ = t2(v1) //@ diag(`should convert v1`)\n\t_ = t3(v1) //@ diag(`should convert v1`)\n}\n"
  },
  {
    "path": "simple/s1016/testdata/go1.9/CheckSimplerStructConversion/convert_alias.go",
    "content": "package pkg\n\ntype S1 struct {\n\tA int\n}\n\ntype S2 struct {\n\tA int\n}\n\ntype Alias = S2\n\n// XXX the diagnostics depend on GODEBUG\n\nfunc foo() {\n\tv1 := S1{A: 1}\n\tv2 := Alias{A: 1}\n\n\t_ = Alias{A: v1.A} //@ diag(`should convert v1`)\n\t_ = S1{A: v2.A}    //@ diag(`should convert v2`)\n}\n"
  },
  {
    "path": "simple/s1016/testdata/go1.9/CheckSimplerStructConversion/convert_alias.go.golden",
    "content": "package pkg\n\ntype S1 struct {\n\tA int\n}\n\ntype S2 struct {\n\tA int\n}\n\ntype Alias = S2\n\n// XXX the diagnostics depend on GODEBUG\n\nfunc foo() {\n\tv1 := S1{A: 1}\n\tv2 := Alias{A: 1}\n\n\t_ = Alias(v1) //@ diag(`should convert v1`)\n\t_ = S1(v2)    //@ diag(`should convert v2`)\n}\n"
  },
  {
    "path": "simple/s1017/s1017.go",
    "content": "package s1017\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"reflect\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1017\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Replace manual trimming with \\'strings.TrimPrefix\\'`,\n\t\tText: `Instead of using \\'strings.HasPrefix\\' and manual slicing, use the\n\\'strings.TrimPrefix\\' function. If the string doesn't start with the\nprefix, the original string will be returned. Using \\'strings.TrimPrefix\\'\nreduces complexity, and avoids common bugs, such as off-by-one\nmistakes.`,\n\t\tBefore: `\nif strings.HasPrefix(str, prefix) {\n    str = str[len(prefix):]\n}`,\n\t\tAfter:   `str = strings.TrimPrefix(str, prefix)`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tsameNonDynamic := func(node1, node2 ast.Node) bool {\n\t\tif reflect.TypeOf(node1) != reflect.TypeOf(node2) {\n\t\t\treturn false\n\t\t}\n\n\t\tswitch node1 := node1.(type) {\n\t\tcase *ast.Ident:\n\t\t\treturn pass.TypesInfo.ObjectOf(node1) == pass.TypesInfo.ObjectOf(node2.(*ast.Ident))\n\t\tcase *ast.SelectorExpr, *ast.IndexExpr:\n\t\t\treturn astutil.Equal(node1, node2)\n\t\tcase *ast.BasicLit:\n\t\t\treturn astutil.Equal(node1, node2)\n\t\t}\n\t\treturn false\n\t}\n\n\tisLenOnIdent := func(fn ast.Expr, ident ast.Expr) bool {\n\t\tcall, ok := fn.(*ast.CallExpr)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\t\tif !code.IsCallTo(pass, call, \"len\") {\n\t\t\treturn false\n\t\t}\n\t\tif len(call.Args) != 1 {\n\t\t\treturn false\n\t\t}\n\t\treturn sameNonDynamic(call.Args[knowledge.Arg(\"len.v\")], ident)\n\t}\n\n\tseen := make(map[ast.Node]struct{})\n\tfn := func(node ast.Node) {\n\t\tvar pkg string\n\t\tvar fun string\n\n\t\tifstmt := node.(*ast.IfStmt)\n\t\tif ifstmt.Init != nil {\n\t\t\treturn\n\t\t}\n\t\tif ifstmt.Else != nil {\n\t\t\tseen[ifstmt.Else] = struct{}{}\n\t\t\treturn\n\t\t}\n\t\tif _, ok := seen[ifstmt]; ok {\n\t\t\treturn\n\t\t}\n\t\tif len(ifstmt.Body.List) != 1 {\n\t\t\treturn\n\t\t}\n\t\tcondCall, ok := ifstmt.Cond.(*ast.CallExpr)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tcondCallName := code.CallName(pass, condCall)\n\t\tswitch condCallName {\n\t\tcase \"strings.HasPrefix\":\n\t\t\tpkg = \"strings\"\n\t\t\tfun = \"HasPrefix\"\n\t\tcase \"strings.HasSuffix\":\n\t\t\tpkg = \"strings\"\n\t\t\tfun = \"HasSuffix\"\n\t\tcase \"strings.Contains\":\n\t\t\tpkg = \"strings\"\n\t\t\tfun = \"Contains\"\n\t\tcase \"bytes.HasPrefix\":\n\t\t\tpkg = \"bytes\"\n\t\t\tfun = \"HasPrefix\"\n\t\tcase \"bytes.HasSuffix\":\n\t\t\tpkg = \"bytes\"\n\t\t\tfun = \"HasSuffix\"\n\t\tcase \"bytes.Contains\":\n\t\t\tpkg = \"bytes\"\n\t\t\tfun = \"Contains\"\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\n\t\tassign, ok := ifstmt.Body.List[0].(*ast.AssignStmt)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tif assign.Tok != token.ASSIGN {\n\t\t\treturn\n\t\t}\n\t\tif len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {\n\t\t\treturn\n\t\t}\n\t\tif !sameNonDynamic(condCall.Args[0], assign.Lhs[0]) {\n\t\t\treturn\n\t\t}\n\n\t\tswitch rhs := assign.Rhs[0].(type) {\n\t\tcase *ast.CallExpr:\n\t\t\tif len(rhs.Args) < 2 || !sameNonDynamic(condCall.Args[0], rhs.Args[0]) || !sameNonDynamic(condCall.Args[1], rhs.Args[1]) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trhsName := code.CallName(pass, rhs)\n\t\t\tif condCallName == \"strings.HasPrefix\" && rhsName == \"strings.TrimPrefix\" ||\n\t\t\t\tcondCallName == \"strings.HasSuffix\" && rhsName == \"strings.TrimSuffix\" ||\n\t\t\t\tcondCallName == \"strings.Contains\" && rhsName == \"strings.Replace\" ||\n\t\t\t\tcondCallName == \"bytes.HasPrefix\" && rhsName == \"bytes.TrimPrefix\" ||\n\t\t\t\tcondCallName == \"bytes.HasSuffix\" && rhsName == \"bytes.TrimSuffix\" ||\n\t\t\t\tcondCallName == \"bytes.Contains\" && rhsName == \"bytes.Replace\" {\n\t\t\t\treport.Report(pass, ifstmt, fmt.Sprintf(\"should replace this if statement with an unconditional %s\", rhsName), report.FilterGenerated())\n\t\t\t}\n\t\tcase *ast.SliceExpr:\n\t\t\tslice := rhs\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif slice.Slice3 {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !sameNonDynamic(slice.X, condCall.Args[0]) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvalidateOffset := func(off ast.Expr) bool {\n\t\t\t\tswitch off := off.(type) {\n\t\t\t\tcase *ast.CallExpr:\n\t\t\t\t\treturn isLenOnIdent(off, condCall.Args[1])\n\t\t\t\tcase *ast.BasicLit:\n\t\t\t\t\tif pkg != \"strings\" {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif _, ok := condCall.Args[1].(*ast.BasicLit); !ok {\n\t\t\t\t\t\t// Only allow manual slicing with an integer\n\t\t\t\t\t\t// literal if the second argument to HasPrefix\n\t\t\t\t\t\t// was a string literal.\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\ts, ok1 := code.ExprToString(pass, condCall.Args[1])\n\t\t\t\t\tn, ok2 := code.ExprToInt(pass, off)\n\t\t\t\t\tif !ok1 || !ok2 || n != int64(len(s)) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\tdefault:\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch fun {\n\t\t\tcase \"HasPrefix\":\n\t\t\t\t// TODO(dh) We could detect a High that is len(s), but another\n\t\t\t\t// rule will already flag that, anyway.\n\t\t\t\tif slice.High != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !validateOffset(slice.Low) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\tcase \"HasSuffix\":\n\t\t\t\tif slice.Low != nil {\n\t\t\t\t\tn, ok := code.ExprToInt(pass, slice.Low)\n\t\t\t\t\tif !ok || n != 0 {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tswitch index := slice.High.(type) {\n\t\t\t\tcase *ast.BinaryExpr:\n\t\t\t\t\tif index.Op != token.SUB {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif !isLenOnIdent(index.X, condCall.Args[0]) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif !validateOffset(index.Y) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvar replacement string\n\t\t\tswitch fun {\n\t\t\tcase \"HasPrefix\":\n\t\t\t\treplacement = \"TrimPrefix\"\n\t\t\tcase \"HasSuffix\":\n\t\t\t\treplacement = \"TrimSuffix\"\n\t\t\t}\n\t\t\treport.Report(pass, ifstmt, fmt.Sprintf(\"should replace this if statement with an unconditional %s.%s\", pkg, replacement),\n\t\t\t\treport.ShortRange(),\n\t\t\t\treport.FilterGenerated())\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.IfStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1017/s1017_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1017\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1017/testdata/go1.0/CheckTrim/trim.go",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n)\n\nfunc foo(s string) int { return 0 }\nfunc gen() string {\n\treturn \"\"\n}\n\nfunc fn() {\n\tconst s1 = \"a string value\"\n\tvar s2 = \"a string value\"\n\tconst n = 14\n\n\tvar id1 = \"a string value\"\n\tvar id2 string\n\tif strings.HasPrefix(id1, s1) { //@ diag(re`should replace.*with.*strings\\.TrimPrefix`)\n\t\tid1 = id1[len(s1):]\n\t}\n\n\tif strings.HasPrefix(id1, s1) { //@ diag(re`should replace.*with.*strings\\.TrimPrefix`)\n\t\tid1 = strings.TrimPrefix(id1, s1)\n\t}\n\n\tif strings.HasPrefix(id1, s1) {\n\t\tid1 = strings.TrimPrefix(id1, s2)\n\t}\n\n\tif strings.Contains(id1, s1) { //@ diag(re`should replace.*with.*strings\\.Replace`)\n\t\tid1 = strings.Replace(id1, s1, \"something\", 123)\n\t}\n\n\tif strings.HasSuffix(id1, s2) { //@ diag(re`should replace.*with.*strings\\.TrimSuffix`)\n\t\tid1 = id1[:len(id1)-len(s2)]\n\t}\n\n\tvar x, y []string\n\tvar i int\n\tif strings.HasPrefix(x[i], s1) { //@ diag(re`should replace.*with.*strings\\.TrimPrefix`)\n\t\tx[i] = x[i][len(s1):]\n\t}\n\n\tif strings.HasPrefix(x[i], y[i]) { //@ diag(re`should replace.*with.*strings\\.TrimPrefix`)\n\t\tx[i] = x[i][len(y[i]):]\n\t}\n\n\tvar t struct{ x string }\n\tif strings.HasPrefix(t.x, s1) { //@ diag(re`should replace.*with.*strings\\.TrimPrefix`)\n\t\tt.x = t.x[len(s1):]\n\t}\n\n\tif strings.HasPrefix(id1, \"test\") { //@ diag(re`should replace.*with.*strings\\.TrimPrefix`)\n\t\tid1 = id1[len(\"test\"):]\n\t}\n\n\tif strings.HasPrefix(id1, \"test\") { //@ diag(re`should replace.*with.*strings\\.TrimPrefix`)\n\t\tid1 = id1[4:]\n\t}\n\n\tif strings.HasPrefix(id1, s1) { // not allowed, 14 and s1 aren't obviously connected\n\t\tid1 = id1[14:]\n\t}\n\n\tif strings.HasPrefix(id1, s1) { // not allowed, s1 and n aren't obviously connected\n\t\tid1 = id1[n:]\n\t}\n\n\tvar b1, b2 []byte\n\tif bytes.HasPrefix(b1, b2) { //@ diag(re`should replace.*with.*bytes\\.TrimPrefix`)\n\t\tb1 = b1[len(b2):]\n\t}\n\n\tid3 := s2\n\tif strings.HasPrefix(id1, id3) { //@ diag(re`should replace.*with.*strings\\.TrimPrefix`)\n\t\tid1 = id1[len(id3):]\n\t}\n\n\tif strings.HasSuffix(id1, s2) {\n\t\tid1 = id1[:len(id1)+len(s2)] // wrong operator\n\t}\n\n\tif strings.HasSuffix(id1, s2) {\n\t\tid1 = id1[:len(s2)-len(id1)] // wrong math\n\t}\n\n\tif strings.HasSuffix(id1, s2) {\n\t\tid1 = id1[:len(id1)-len(id1)] // wrong string length\n\t}\n\n\tif strings.HasPrefix(id1, gen()) {\n\t\tid1 = id1[len(gen()):] // dynamic id3\n\t}\n\n\tif strings.HasPrefix(id1, s1) {\n\t\tid1 = id1[foo(s1):] // wrong function\n\t}\n\n\tif strings.HasPrefix(id1, s1) {\n\t\tid1 = id1[len(id1):] // len() on wrong value\n\t}\n\n\tif strings.HasPrefix(id1, \"test\") {\n\t\tid1 = id1[5:] // wrong length\n\t}\n\n\tif strings.HasPrefix(id1, s1) {\n\t\tid1 = id1[len(s1)+1:] // wrong length due to math\n\t}\n\n\tif strings.HasPrefix(id1, s1) {\n\t\tid2 = id1[len(s1):] // assigning to the wrong variable\n\t}\n\n\tif strings.HasPrefix(id1, s1) {\n\t\tid1 = id1[len(s1):15] // has a max\n\t}\n\n\tif strings.HasPrefix(id1, s1) {\n\t\tid1 = id2[len(s1):] // assigning the wrong value\n\t}\n\n\tif strings.HasPrefix(id1, s1) {\n\t\tid1 = id1[len(s1):]\n\t\tid1 += \"\" // doing more work in the if\n\t}\n\n\tif strings.HasPrefix(id1, s1) {\n\t\tid1 = id1[len(s1):]\n\t} else {\n\t\tid1 = \"game over\" // else branch\n\t}\n\n\tif strings.HasPrefix(id1, s1) {\n\t\t// the conditional is guarding additional code\n\t\tid1 = id1[len(s1):]\n\t\tprintln(id1)\n\t}\n\n\tif strings.Contains(id1, s1) {\n\t\tid1 = id1[:]\n\t}\n}\n\nfunc fn2() {\n\tvar s string\n\tconst id = \".json\"\n\n\tif strings.HasSuffix(s, \".json\") { //@ diag(re`should replace.*with.*strings\\.TrimSuffix`)\n\t\ts = strings.TrimSuffix(s, \".json\")\n\t}\n\n\tif strings.HasSuffix(s, \".json\") { //@ diag(re`should replace.*with.*strings\\.TrimSuffix`)\n\t\ts = s[:len(s)-len(\".json\")]\n\t}\n\n\tif strings.HasSuffix(s, \".json\") { //@ diag(re`should replace.*with.*strings\\.TrimSuffix`)\n\t\ts = s[:len(s)-5]\n\t}\n\n\tif strings.HasSuffix(s, id) {\n\t\ts = s[:len(s)-5] // second argument of HasSuffix it not a string literal\n\t}\n\n\tif strings.HasSuffix(s, \".json\") {\n\t\ts = s[:len(s)-4] // wrong length\n\t}\n\n\t// Don't check with else if branch; see https://staticcheck.dev/issues/1447\n\tif strings.HasPrefix(s, \"\\xff\\xfe\") {\n\t\ts = s[2:]\n\t} else if strings.HasPrefix(s, \"\\xef\\xbb\\xbf\") {\n\t\ts = s[3:]\n\t}\n}\n\nfunc fn3() {\n\tconst s1 = \"a string value\"\n\n\tvar id1 = \"a string value\"\n\tlen := func(string) int { return 0 } // don't accept non-builtin definition of len\n\tif strings.HasPrefix(id1, s1) {\n\t\tid1 = id1[len(s1):]\n\t}\n\n\tif strings.HasSuffix(id1, s1) {\n\t\tid1 = id1[:len(id1)-len(s1)]\n\t}\n}\n"
  },
  {
    "path": "simple/s1018/s1018.go",
    "content": "package s1018\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1018\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Use \\\"copy\\\" for sliding elements`,\n\t\tText: `\\'copy()\\' permits using the same source and destination slice, even with\noverlapping ranges. This makes it ideal for sliding elements in a\nslice.`,\n\n\t\tBefore: `\nfor i := 0; i < n; i++ {\n    bs[i] = bs[offset+i]\n}`,\n\t\tAfter:   `copy(bs[:n], bs[offset:])`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckLoopSlideQ = pattern.MustParse(`\n\t\t(ForStmt\n\t\t\t(AssignStmt initvar@(Ident _) _ (IntegerLiteral \"0\"))\n\t\t\t(BinaryExpr initvar \"<\" limit@(Ident _))\n\t\t\t(IncDecStmt initvar \"++\")\n\t\t\t[(AssignStmt\n\t\t\t\t(IndexExpr slice@(Ident _) initvar)\n\t\t\t\t\"=\"\n\t\t\t\t(IndexExpr slice (BinaryExpr offset@(Ident _) \"+\" initvar)))])`)\n\tcheckLoopSlideR = pattern.MustParse(`\n\t\t(CallExpr\n\t\t\t(Ident \"copy\")\n\t\t\t[(SliceExpr slice nil limit nil)\n\t\t\t\t(SliceExpr slice offset nil nil)])`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// TODO(dh): detect bs[i+offset] in addition to bs[offset+i]\n\t// TODO(dh): consider merging this function with LintLoopCopy\n\t// TODO(dh): detect length that is an expression, not a variable name\n\t// TODO(dh): support sliding to a different offset than the beginning of the slice\n\n\tfor node, m := range code.Matches(pass, checkLoopSlideQ) {\n\t\ttyp := pass.TypesInfo.TypeOf(m.State[\"slice\"].(*ast.Ident))\n\t\t// The pattern probably needs a core type, but All is fine, too. Either way we only accept slices.\n\t\tif !typeutil.All(typ, typeutil.IsSlice) {\n\t\t\tcontinue\n\t\t}\n\n\t\tedits := code.EditMatch(pass, node, m, checkLoopSlideR)\n\t\treport.Report(pass, node, \"should use copy() instead of loop for sliding slice elements\",\n\t\t\treport.ShortRange(),\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(\"Use copy() instead of loop\", edits...)))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1018/s1018_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1018\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1018/testdata/go1.0/CheckLoopSlide/LintLoopSlide.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar n int\n\tvar bs []int\n\tvar offset int\n\n\tfor i := 0; i < n; i++ { //@ diag(`should use copy() instead of loop for sliding slice elements`)\n\t\tbs[i] = bs[offset+i]\n\t}\n\n\tfor i := 1; i < n; i++ { // not currently supported\n\t\tbs[i] = bs[offset+i]\n\t}\n\n\tfor i := 1; i < n; i++ { // not currently supported\n\t\tbs[i] = bs[i+offset]\n\t}\n\n\tfor i := 0; i <= n; i++ {\n\t\tbs[i] = bs[offset+i]\n\t}\n}\n"
  },
  {
    "path": "simple/s1018/testdata/go1.0/CheckLoopSlide/LintLoopSlide.go.golden",
    "content": "package pkg\n\nfunc fn() {\n\tvar n int\n\tvar bs []int\n\tvar offset int\n\n\tcopy(bs[:n], bs[offset:])\n\n\tfor i := 1; i < n; i++ { // not currently supported\n\t\tbs[i] = bs[offset+i]\n\t}\n\n\tfor i := 1; i < n; i++ { // not currently supported\n\t\tbs[i] = bs[i+offset]\n\t}\n\n\tfor i := 0; i <= n; i++ {\n\t\tbs[i] = bs[offset+i]\n\t}\n}\n"
  },
  {
    "path": "simple/s1018/testdata/go1.18/CheckLoopSlide/generics.go",
    "content": "package pkg\n\nfunc tpfn[T []int]() {\n\tvar n int\n\tvar bs T\n\tvar offset int\n\n\tfor i := 0; i < n; i++ { //@ diag(`should use copy() instead of loop for sliding slice elements`)\n\t\tbs[i] = bs[offset+i]\n\t}\n}\n"
  },
  {
    "path": "simple/s1018/testdata/go1.18/CheckLoopSlide/generics.go.golden",
    "content": "package pkg\n\nfunc tpfn[T []int]() {\n\tvar n int\n\tvar bs T\n\tvar offset int\n\n\tcopy(bs[:n], bs[offset:])\n}\n"
  },
  {
    "path": "simple/s1019/s1019.go",
    "content": "package s1019\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\t\"path/filepath\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1019\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Simplify \\\"make\\\" call by omitting redundant arguments`,\n\t\tText: `The \\\"make\\\" function has default values for the length and capacity\narguments. For channels, the length defaults to zero, and for slices,\nthe capacity defaults to the length.`,\n\t\tSince: \"2017.1\",\n\t\t// MergeIfAll because the type might be different under different build tags.\n\t\t// You shouldn't write code like that…\n\t\tMergeIf: lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckMakeLenCapQ1 = pattern.MustParse(`(CallExpr (Builtin \"make\") [typ size@(IntegerLiteral \"0\")])`)\n\tcheckMakeLenCapQ2 = pattern.MustParse(`(CallExpr (Builtin \"make\") [typ size size])`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tif pass.Pkg.Path() == \"runtime_test\" && filepath.Base(pass.Fset.Position(node.Pos()).Filename) == \"map_test.go\" {\n\t\t\t// special case of runtime tests testing map creation\n\t\t\treturn\n\t\t}\n\t\tif m, ok := code.Match(pass, checkMakeLenCapQ1, node); ok {\n\t\t\tT := m.State[\"typ\"].(ast.Expr)\n\t\t\tsize := m.State[\"size\"].(ast.Node)\n\n\t\t\tif _, ok := typeutil.CoreType(pass.TypesInfo.TypeOf(T)).Underlying().(*types.Chan); ok {\n\t\t\t\treport.Report(pass, size, fmt.Sprintf(\"should use make(%s) instead\", report.Render(pass, T)), report.FilterGenerated())\n\t\t\t}\n\n\t\t} else if m, ok := code.Match(pass, checkMakeLenCapQ2, node); ok {\n\t\t\t// TODO(dh): don't consider sizes identical if they're\n\t\t\t// dynamic. for example: make(T, <-ch, <-ch).\n\t\t\tT := m.State[\"typ\"].(ast.Expr)\n\t\t\tsize := m.State[\"size\"].(ast.Node)\n\t\t\treport.Report(pass, size,\n\t\t\t\tfmt.Sprintf(\"should use make(%s, %s) instead\", report.Render(pass, T), report.Render(pass, size)),\n\t\t\t\treport.FilterGenerated())\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.CallExpr)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1019/s1019_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1019\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1019/testdata/go1.0/CheckMakeLenCap/LintMakeLenCap.go",
    "content": "package pkg\n\nfunc fn() {\n\tconst c = 0\n\tvar x, y int\n\ttype s []int\n\ttype ch chan int\n\t_ = make([]int, 1)\n\t_ = make([]int, 0)    // length is mandatory for slices, don't suggest removal\n\t_ = make(s, 0)        // length is mandatory for slices, don't suggest removal\n\t_ = make(chan int, c) // constant of 0 may be due to debugging, math or platform-specific code\n\t_ = make(chan int, 0) //@ diag(`should use make(chan int) instead`)\n\t_ = make(ch, 0)       //@ diag(`should use make(ch) instead`)\n\t_ = make(map[int]int, 0)\n\t_ = make([]int, 1, 1) //@ diag(`should use make([]int, 1) instead`)\n\t_ = make([]int, x, x) //@ diag(`should use make([]int, x) instead`)\n\t_ = make([]int, 1, 2)\n\t_ = make([]int, x, y)\n}\n"
  },
  {
    "path": "simple/s1019/testdata/go1.18/CheckMakeLenCap/CheckMakeLenCap.go",
    "content": "package pkg\n\nfunc fn1() {\n\t_ = make(chan int, 0) //@ diag(`should use make(chan int) instead`)\n}\n\nfunc fn2[T chan int]() {\n\t_ = make(T, 0) //@ diag(`should use make(T) instead`)\n}\n\nfunc fn3[T chan T]() {\n\t_ = make(T, 0) //@ diag(`should use make(T) instead`)\n}\n\nfunc fn4[T any, C chan T]() {\n\t_ = make(chan T, 0) //@ diag(`should use make(chan T) instead`)\n\t_ = make(C, 0)      //@ diag(`should use make(C) instead`)\n}\n\nfunc fn5[T []int]() {\n\t_ = make(T, 0) // don't flag this, T isn't a channel\n}\n\ntype I interface {\n\tchan int\n}\n\nfunc fn6[T I]() {\n\t_ = make(T, 0) //@ diag(`should use make(T) instead`)\n}\n"
  },
  {
    "path": "simple/s1020/s1020.go",
    "content": "package s1020\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1020\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:   `Omit redundant nil check in type assertion`,\n\t\tBefore:  `if _, ok := i.(T); ok && i != nil {}`,\n\t\tAfter:   `if _, ok := i.(T); ok {}`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckAssertNotNilFn1Q = pattern.MustParse(`\n\t\t(IfStmt\n\t\t\t(AssignStmt [(Ident \"_\") ok@(Object _)] _ [(TypeAssertExpr assert@(Object _) _)])\n\t\t\t(Or\n\t\t\t\t(BinaryExpr ok \"&&\" (BinaryExpr assert \"!=\" (Builtin \"nil\")))\n\t\t\t\t(BinaryExpr (BinaryExpr assert \"!=\" (Builtin \"nil\")) \"&&\" ok))\n\t\t\t_\n\t\t\t_)`)\n\tcheckAssertNotNilFn2Q = pattern.MustParse(`\n\t\t(IfStmt\n\t\t\tnil\n\t\t\t(BinaryExpr lhs@(Object _) \"!=\" (Builtin \"nil\"))\n\t\t\t[\n\t\t\t\tifstmt@(IfStmt\n\t\t\t\t\t(AssignStmt [(Ident \"_\") ok@(Object _)] _ [(TypeAssertExpr lhs _)])\n\t\t\t\t\tok\n\t\t\t\t\t_\n\t\t\t\t\tnil)\n\t\t\t]\n\t\t\tnil)`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkAssertNotNilFn1Q) {\n\t\tassert := m.State[\"assert\"].(types.Object)\n\t\tassign := m.State[\"ok\"].(types.Object)\n\t\treport.Report(pass, node, fmt.Sprintf(\"when %s is true, %s can't be nil\", assign.Name(), assert.Name()),\n\t\t\treport.ShortRange(),\n\t\t\treport.FilterGenerated())\n\t}\n\tfor _, m := range code.Matches(pass, checkAssertNotNilFn2Q) {\n\t\tifstmt := m.State[\"ifstmt\"].(*ast.IfStmt)\n\t\tlhs := m.State[\"lhs\"].(types.Object)\n\t\tassignIdent := m.State[\"ok\"].(types.Object)\n\t\treport.Report(pass, ifstmt, fmt.Sprintf(\"when %s is true, %s can't be nil\", assignIdent.Name(), lhs.Name()),\n\t\t\treport.ShortRange(),\n\t\t\treport.FilterGenerated())\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1020/s1020_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1020\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1020/testdata/go1.0/CheckAssertNotNil/LintAssertNotNil.go",
    "content": "package pkg\n\nfunc fn(i interface{}, x interface{}) {\n\tif _, ok := i.(string); ok && i != nil { //@ diag(`when ok is true, i can't be nil`)\n\t}\n\tif _, ok := i.(string); i != nil && ok { //@ diag(`when ok is true, i can't be nil`)\n\t}\n\tif _, ok := i.(string); i != nil || ok {\n\t}\n\tif _, ok := i.(string); i != nil && !ok {\n\t}\n\tif _, ok := i.(string); i == nil && ok {\n\t}\n\tif i != nil {\n\t\tif _, ok := i.(string); ok { //@ diag(`when ok is true, i can't be nil`)\n\t\t}\n\t}\n\tif i != nil {\n\t\t// This gets flagged because of https://github.com/dominikh/go-tools/issues/1047\n\t\tif _, ok := i.(string); ok { //@ diag(`when ok is true, i can't be nil`)\n\t\t} else {\n\t\t\t//\n\t\t}\n\t}\n\tif i != nil {\n\t\tif _, ok := i.(string); ok {\n\t\t} else {\n\t\t\tprintln()\n\t\t}\n\t}\n\tif i != nil {\n\t\tif _, ok := i.(string); ok {\n\t\t}\n\t\tprintln(i)\n\t}\n\tif i == nil {\n\t\tif _, ok := i.(string); ok {\n\t\t}\n\t}\n\tif i != nil {\n\t\tif _, ok := i.(string); !ok {\n\t\t}\n\t}\n\tif x != nil {\n\t\tif _, ok := i.(string); ok {\n\t\t}\n\t}\n\tif i != nil {\n\t\tif _, ok := x.(string); ok {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "simple/s1021/s1021.go",
    "content": "package s1021\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1021\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Merge variable declaration and assignment`,\n\t\tBefore: `\nvar x uint\nx = 1`,\n\t\tAfter:   `var x uint = 1`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\thasMultipleAssignments := func(root ast.Node, ident *ast.Ident) bool {\n\t\tnum := 0\n\t\tast.Inspect(root, func(node ast.Node) bool {\n\t\t\tif num >= 2 {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tassign, ok := node.(*ast.AssignStmt)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tfor _, lhs := range assign.Lhs {\n\t\t\t\tif oident, ok := lhs.(*ast.Ident); ok {\n\t\t\t\t\tif pass.TypesInfo.ObjectOf(oident) == pass.TypesInfo.ObjectOf(ident) {\n\t\t\t\t\t\tnum++\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true\n\t\t})\n\t\treturn num >= 2\n\t}\n\tfn := func(node ast.Node) {\n\t\tblock := node.(*ast.BlockStmt)\n\t\tif len(block.List) < 2 {\n\t\t\treturn\n\t\t}\n\t\tfor i, stmt := range block.List[:len(block.List)-1] {\n\t\t\t_ = i\n\t\t\tdecl, ok := stmt.(*ast.DeclStmt)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tgdecl, ok := decl.Decl.(*ast.GenDecl)\n\t\t\tif !ok || gdecl.Tok != token.VAR || len(gdecl.Specs) != 1 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvspec, ok := gdecl.Specs[0].(*ast.ValueSpec)\n\t\t\tif !ok || len(vspec.Names) != 1 || len(vspec.Values) != 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tassign, ok := block.List[i+1].(*ast.AssignStmt)\n\t\t\tif !ok || assign.Tok != token.ASSIGN {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tident, ok := assign.Lhs[0].(*ast.Ident)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif pass.TypesInfo.ObjectOf(vspec.Names[0]) != pass.TypesInfo.ObjectOf(ident) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif code.RefersTo(pass, assign.Rhs[0], pass.TypesInfo.ObjectOf(ident)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif hasMultipleAssignments(block, ident) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tr := &ast.GenDecl{\n\t\t\t\tSpecs: []ast.Spec{\n\t\t\t\t\t&ast.ValueSpec{\n\t\t\t\t\t\tNames:  vspec.Names,\n\t\t\t\t\t\tValues: []ast.Expr{assign.Rhs[0]},\n\t\t\t\t\t\tType:   vspec.Type,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tTok: gdecl.Tok,\n\t\t\t}\n\t\t\treport.Report(pass, decl, \"should merge variable declaration with assignment on next line\",\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.Fixes(edit.Fix(\"Merge declaration with assignment\", edit.ReplaceWithNode(pass.Fset, edit.Range{decl.Pos(), assign.End()}, r))))\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.BlockStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1021/s1021_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1021\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1021/testdata/go1.0/CheckDeclareAssign/LintDeclareAssign.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar x int //@ diag(`should merge variable declaration with assignment on next line`)\n\tx = 1\n\t_ = x\n\n\tvar y interface{} //@ diag(`should merge variable declaration with assignment on next line`)\n\ty = 1\n\t_ = y\n\n\tif true {\n\t\tvar x string //@ diag(`should merge variable declaration with assignment on next line`)\n\t\tx = \"\"\n\t\t_ = x\n\t}\n\n\tvar z []string\n\tz = append(z, \"\")\n\t_ = z\n\n\tvar f func()\n\tf = func() { f() }\n\t_ = f\n\n\tvar a int\n\ta = 1\n\ta = 2\n\t_ = a\n\n\tvar b int\n\tb = 1\n\t// do stuff\n\tb = 2\n\t_ = b\n\n\tvar c int\n\tunrelated = 1\n\t_ = c\n}\n\nvar unrelated int\n"
  },
  {
    "path": "simple/s1021/testdata/go1.0/CheckDeclareAssign/LintDeclareAssign.go.golden",
    "content": "package pkg\n\nfunc fn() {\n\tvar x int = 1\n\t_ = x\n\n\tvar y interface{} = 1\n\t_ = y\n\n\tif true {\n\t\tvar x string = \"\"\n\t\t_ = x\n\t}\n\n\tvar z []string\n\tz = append(z, \"\")\n\t_ = z\n\n\tvar f func()\n\tf = func() { f() }\n\t_ = f\n\n\tvar a int\n\ta = 1\n\ta = 2\n\t_ = a\n\n\tvar b int\n\tb = 1\n\t// do stuff\n\tb = 2\n\t_ = b\n\n\tvar c int\n\tunrelated = 1\n\t_ = c\n}\n\nvar unrelated int\n"
  },
  {
    "path": "simple/s1023/s1023.go",
    "content": "package s1023\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1023\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Omit redundant control flow`,\n\t\tText: `Functions that have no return value do not need a return statement as\nthe final statement of the function.\n\nSwitches in Go do not have automatic fallthrough, unlike languages\nlike C. It is not necessary to have a break statement as the final\nstatement in a case block.`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn1 := func(node ast.Node) {\n\t\tclause := node.(*ast.CaseClause)\n\t\tif len(clause.Body) < 2 {\n\t\t\treturn\n\t\t}\n\t\tbranch, ok := clause.Body[len(clause.Body)-1].(*ast.BranchStmt)\n\t\tif !ok || branch.Tok != token.BREAK || branch.Label != nil {\n\t\t\treturn\n\t\t}\n\t\treport.Report(pass, branch, \"redundant break statement\", report.FilterGenerated())\n\t}\n\tfn2 := func(node ast.Node) {\n\t\tvar ret *ast.FieldList\n\t\tvar body *ast.BlockStmt\n\t\tswitch x := node.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\tret = x.Type.Results\n\t\t\tbody = x.Body\n\t\tcase *ast.FuncLit:\n\t\t\tret = x.Type.Results\n\t\t\tbody = x.Body\n\t\tdefault:\n\t\t\tlint.ExhaustiveTypeSwitch(node)\n\t\t}\n\t\t// if the func has results, a return can't be redundant.\n\t\t// similarly, if there are no statements, there can be\n\t\t// no return.\n\t\tif ret != nil || body == nil || len(body.List) < 1 {\n\t\t\treturn\n\t\t}\n\t\trst, ok := body.List[len(body.List)-1].(*ast.ReturnStmt)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\t// we don't need to check rst.Results as we already\n\t\t// checked x.Type.Results to be nil.\n\t\treport.Report(pass, rst, \"redundant return statement\", report.FilterGenerated())\n\t}\n\tcode.Preorder(pass, fn1, (*ast.CaseClause)(nil))\n\tcode.Preorder(pass, fn2, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1023/s1023_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1023\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1023/testdata/go1.0/CheckRedundantBreak/LintRedundantBreak.go",
    "content": "package pkg\n\nfunc fn(x int) {\n\tswitch x {\n\tcase 1:\n\t\tprintln()\n\t\tbreak //@ diag(`redundant break`)\n\tcase 2:\n\t\tprintln()\n\tcase 3:\n\t\tbreak // don't flag cases only consisting of break\n\tcase 4:\n\t\tprintln()\n\t\tbreak\n\t\tprintln()\n\tcase 5:\n\t\tprintln()\n\t\tif true {\n\t\t\tbreak // we don't currently detect this\n\t\t}\n\tcase 6:\n\t\tprintln()\n\t\tif true {\n\t\t\tbreak\n\t\t}\n\t\tprintln()\n\t}\n\nlabel:\n\tfor {\n\t\tswitch x {\n\t\tcase 1:\n\t\t\tprintln()\n\t\t\tbreak label\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "simple/s1023/testdata/go1.0/CheckRedundantReturn/LintRedundantReturn.go",
    "content": "package pkg\n\nfunc fn1() {\n\treturn //@ diag(`redundant return`)\n}\n\nfunc fn2(a int) {\n\treturn //@ diag(`redundant return`)\n}\n\nfunc fn3() int {\n\treturn 3\n}\n\nfunc fn4() (n int) {\n\treturn\n}\n\nfunc fn5(b bool) {\n\tif b {\n\t\treturn\n\t}\n}\n\nfunc fn6() {\n\treturn\n\tprintln(\"foo\")\n}\n\nfunc fn7() {\n\treturn\n\tprintln(\"foo\")\n\treturn //@ diag(`redundant return`)\n}\n\nfunc fn8() {\n\t_ = func() {\n\t\treturn //@ diag(`redundant return`)\n\t}\n}\n"
  },
  {
    "path": "simple/s1024/s1024.go",
    "content": "package s1024\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1024\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Replace \\'x.Sub(time.Now())\\' with \\'time.Until(x)\\'`,\n\t\tText: `The \\'time.Until\\' helper has the same effect as using \\'x.Sub(time.Now())\\'\nbut is easier to read.`,\n\t\tBefore:  `x.Sub(time.Now())`,\n\t\tAfter:   `time.Until(x)`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckTimeUntilQ = pattern.MustParse(`(CallExpr (Symbol \"(time.Time).Sub\") [(CallExpr (Symbol \"time.Now\") [])])`)\n\tcheckTimeUntilR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident \"time\") (Ident \"Until\")) [arg])`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node := range code.Matches(pass, checkTimeUntilQ) {\n\t\tif sel, ok := node.(*ast.CallExpr).Fun.(*ast.SelectorExpr); ok {\n\t\t\tr := pattern.NodeToAST(checkTimeUntilR.Root, map[string]any{\"arg\": sel.X}).(ast.Node)\n\t\t\treport.Report(pass, node, \"should use time.Until instead of t.Sub(time.Now())\",\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.MinimumStdlibVersion(\"go1.8\"),\n\t\t\t\treport.Fixes(edit.Fix(\"Replace with call to time.Until\", edit.ReplaceWithNode(pass.Fset, node, r))))\n\t\t} else {\n\t\t\treport.Report(pass, node, \"should use time.Until instead of t.Sub(time.Now())\",\n\t\t\t\treport.MinimumStdlibVersion(\"go1.8\"),\n\t\t\t\treport.FilterGenerated())\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1024/s1024_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1024\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1024/testdata/go1.7/CheckTimeUntil/LimeTimeUntil.go",
    "content": "package pkg\n\nimport \"time\"\n\nfunc fn(t time.Time) {\n\tt.Sub(time.Now())\n}\n"
  },
  {
    "path": "simple/s1024/testdata/go1.8/CheckTimeUntil/LimeTimeUntil.go",
    "content": "package pkg\n\nimport \"time\"\n\nfunc fn(t time.Time) {\n\tt.Sub(time.Now()) //@ diag(`time.Until`)\n\tt.Sub(t)\n\tt2 := time.Now()\n\tt.Sub(t2)\n}\n"
  },
  {
    "path": "simple/s1024/testdata/go1.8/CheckTimeUntil/LimeTimeUntil.go.golden",
    "content": "package pkg\n\nimport \"time\"\n\nfunc fn(t time.Time) {\n\ttime.Until(t) //@ diag(`time.Until`)\n\tt.Sub(t)\n\tt2 := time.Now()\n\tt.Sub(t2)\n}\n"
  },
  {
    "path": "simple/s1025/s1025.go",
    "content": "package s1025\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/exp/typeparams\"\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName: \"S1025\",\n\t\tRun:  run,\n\t\tRequires: append([]*analysis.Analyzer{\n\t\t\tbuildir.Analyzer,\n\t\t\tgenerated.Analyzer,\n\t\t}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Don't use \\'fmt.Sprintf(\"%s\", x)\\' unnecessarily`,\n\t\tText: `In many instances, there are easier and more efficient ways of getting\na value's string representation. Whenever a value's underlying type is\na string already, or the type has a String method, they should be used\ndirectly.\n\nGiven the following shared definitions\n\n    type T1 string\n    type T2 int\n\n    func (T2) String() string { return \"Hello, world\" }\n\n    var x string\n    var y T1\n    var z T2\n\nwe can simplify\n\n    fmt.Sprintf(\"%s\", x)\n    fmt.Sprintf(\"%s\", y)\n    fmt.Sprintf(\"%s\", z)\n\nto\n\n    x\n    string(y)\n    z.String()\n`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkRedundantSprintfQ = pattern.MustParse(`(CallExpr (Symbol \"fmt.Sprintf\") [format arg])`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkRedundantSprintfQ) {\n\t\tformat := m.State[\"format\"].(ast.Expr)\n\t\targ := m.State[\"arg\"].(ast.Expr)\n\t\t// TODO(dh): should we really support named constants here?\n\t\t// shouldn't we only look for string literals? to avoid false\n\t\t// positives via build tags?\n\t\tif s, ok := code.ExprToString(pass, format); !ok || s != \"%s\" {\n\t\t\tcontinue\n\t\t}\n\t\ttyp := pass.TypesInfo.TypeOf(arg)\n\t\tif typeparams.IsTypeParam(typ) {\n\t\t\tcontinue\n\t\t}\n\t\tirpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg\n\n\t\tif typeutil.IsTypeWithName(typ, \"reflect.Value\") {\n\t\t\t// printing with %s produces output different from using\n\t\t\t// the String method\n\t\t\tcontinue\n\t\t}\n\n\t\tif isFormatter(typ, &irpkg.Prog.MethodSets) {\n\t\t\t// the type may choose to handle %s in arbitrary ways\n\t\t\tcontinue\n\t\t}\n\n\t\tif types.Implements(typ, knowledge.Interfaces[\"fmt.Stringer\"]) {\n\t\t\treplacement := &ast.CallExpr{\n\t\t\t\tFun: &ast.SelectorExpr{\n\t\t\t\t\tX:   arg,\n\t\t\t\t\tSel: &ast.Ident{Name: \"String\"},\n\t\t\t\t},\n\t\t\t}\n\t\t\treport.Report(pass, node, \"should use String() instead of fmt.Sprintf\",\n\t\t\t\treport.Fixes(edit.Fix(\"Replace with call to String method\", edit.ReplaceWithNode(pass.Fset, node, replacement))))\n\t\t} else if types.Unalias(typ) == types.Universe.Lookup(\"string\").Type() {\n\t\t\treport.Report(pass, node, \"the argument is already a string, there's no need to use fmt.Sprintf\",\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.Fixes(edit.Fix(\"Remove unnecessary call to fmt.Sprintf\", edit.ReplaceWithNode(pass.Fset, node, arg))))\n\t\t} else if typ.Underlying() == types.Universe.Lookup(\"string\").Type() {\n\t\t\treplacement := &ast.CallExpr{\n\t\t\t\tFun:  &ast.Ident{Name: \"string\"},\n\t\t\t\tArgs: []ast.Expr{arg},\n\t\t\t}\n\t\t\treport.Report(pass, node, \"the argument's underlying type is a string, should use a simple conversion instead of fmt.Sprintf\",\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.Fixes(edit.Fix(\"Replace with conversion to string\", edit.ReplaceWithNode(pass.Fset, node, replacement))))\n\t\t} else if code.IsOfStringConvertibleByteSlice(pass, arg) {\n\t\t\treplacement := &ast.CallExpr{\n\t\t\t\tFun:  &ast.Ident{Name: \"string\"},\n\t\t\t\tArgs: []ast.Expr{arg},\n\t\t\t}\n\t\t\treport.Report(pass, node, \"the argument's underlying type is a slice of bytes, should use a simple conversion instead of fmt.Sprintf\",\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.Fixes(edit.Fix(\"Replace with conversion to string\", edit.ReplaceWithNode(pass.Fset, node, replacement))))\n\t\t}\n\n\t}\n\treturn nil, nil\n}\n\nfunc isFormatter(T types.Type, msCache *typeutil.MethodSetCache) bool {\n\t// TODO(dh): this function also exists in staticcheck/lint.go – deduplicate.\n\n\tms := msCache.MethodSet(T)\n\tsel := ms.Lookup(nil, \"Format\")\n\tif sel == nil {\n\t\treturn false\n\t}\n\tfn, ok := sel.Obj().(*types.Func)\n\tif !ok {\n\t\t// should be unreachable\n\t\treturn false\n\t}\n\tsig := fn.Type().(*types.Signature)\n\tif sig.Params().Len() != 2 {\n\t\treturn false\n\t}\n\t// TODO(dh): check the types of the arguments for more\n\t// precision\n\tif sig.Results().Len() != 0 {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "simple/s1025/s1025_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1025\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1025/testdata/go1.0/CheckRedundantSprintf/LintRedundantSprintf.go",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype T1 string\ntype T2 T1\ntype T3 int\ntype T4 int\ntype T5 int\ntype T6 string\ntype T7 []byte\n\ntype T9 string\ntype T10 []byte\ntype T11 int\n\ntype MyByte byte\n\nfunc (T3) String() string        { return \"\" }\nfunc (T6) String() string        { return \"\" }\nfunc (T4) String(arg int) string { return \"\" }\nfunc (T5) String()               {}\n\nfunc (T9) Format(f fmt.State, c rune)  {}\nfunc (T10) Format(f fmt.State, c rune) {}\nfunc (T11) Format(f fmt.State, c rune) {}\nfunc (T11) String() string             { return \"\" }\n\nfunc fn() {\n\tvar t1 T1\n\tvar t2 T2\n\tvar t3 T3\n\tvar t4 T4\n\tvar t5 T5\n\tvar t6 T6\n\tvar t7 T7\n\tvar t9 T9\n\tvar t10 T10\n\tvar t11 T11\n\n\t_ = fmt.Sprintf(\"%s\", \"test\")      //@ diag(`is already a string`)\n\t_ = fmt.Sprintf(\"%s\", t1)          //@ diag(`is a string`)\n\t_ = fmt.Sprintf(\"%s\", t2)          //@ diag(`is a string`)\n\t_ = fmt.Sprintf(\"%s\", t3)          //@ diag(`should use String() instead of fmt.Sprintf`)\n\t_ = fmt.Sprintf(\"%s\", t3.String()) //@ diag(`is already a string`)\n\t_ = fmt.Sprintf(\"%s\", t4)\n\t_ = fmt.Sprintf(\"%s\", t5)\n\t_ = fmt.Sprintf(\"%s %s\", t1, t2)\n\t_ = fmt.Sprintf(\"%v\", t1)\n\t_ = fmt.Sprintf(\"%s\", t6) //@ diag(`should use String() instead of fmt.Sprintf`)\n\t_ = fmt.Sprintf(\"%s\", t7) //@ diag(`underlying type is a slice of bytes`)\n\n\t// don't simplify types that implement fmt.Formatter\n\t_ = fmt.Sprintf(\"%s\", t9)\n\t_ = fmt.Sprintf(\"%s\", t10)\n\t_ = fmt.Sprintf(\"%s\", t11)\n}\n"
  },
  {
    "path": "simple/s1025/testdata/go1.0/CheckRedundantSprintf/LintRedundantSprintf.go.golden",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype T1 string\ntype T2 T1\ntype T3 int\ntype T4 int\ntype T5 int\ntype T6 string\ntype T7 []byte\n\ntype T9 string\ntype T10 []byte\ntype T11 int\n\ntype MyByte byte\n\nfunc (T3) String() string        { return \"\" }\nfunc (T6) String() string        { return \"\" }\nfunc (T4) String(arg int) string { return \"\" }\nfunc (T5) String()               {}\n\nfunc (T9) Format(f fmt.State, c rune)  {}\nfunc (T10) Format(f fmt.State, c rune) {}\nfunc (T11) Format(f fmt.State, c rune) {}\nfunc (T11) String() string             { return \"\" }\n\nfunc fn() {\n\tvar t1 T1\n\tvar t2 T2\n\tvar t3 T3\n\tvar t4 T4\n\tvar t5 T5\n\tvar t6 T6\n\tvar t7 T7\n\tvar t9 T9\n\tvar t10 T10\n\tvar t11 T11\n\n\t_ = \"test\"      //@ diag(`is already a string`)\n\t_ = string(t1)  //@ diag(`is a string`)\n\t_ = string(t2)  //@ diag(`is a string`)\n\t_ = t3.String() //@ diag(`should use String() instead of fmt.Sprintf`)\n\t_ = t3.String() //@ diag(`is already a string`)\n\t_ = fmt.Sprintf(\"%s\", t4)\n\t_ = fmt.Sprintf(\"%s\", t5)\n\t_ = fmt.Sprintf(\"%s %s\", t1, t2)\n\t_ = fmt.Sprintf(\"%v\", t1)\n\t_ = t6.String() //@ diag(`should use String() instead of fmt.Sprintf`)\n\t_ = string(t7)  //@ diag(`underlying type is a slice of bytes`)\n\n\t// don't simplify types that implement fmt.Formatter\n\t_ = fmt.Sprintf(\"%s\", t9)\n\t_ = fmt.Sprintf(\"%s\", t10)\n\t_ = fmt.Sprintf(\"%s\", t11)\n}\n"
  },
  {
    "path": "simple/s1025/testdata/go1.17/CheckRedundantSprintf/LintRedundantSprintf.go",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype MyByte byte\ntype T1 []MyByte\n\nfunc fn() {\n\tvar t1 T1\n\t_ = fmt.Sprintf(\"%s\", t1)\n}\n"
  },
  {
    "path": "simple/s1025/testdata/go1.17/CheckRedundantSprintf/LintRedundantSprintf.go.golden",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype MyByte byte\ntype T1 []MyByte\n\nfunc fn() {\n\tvar t1 T1\n\t_ = fmt.Sprintf(\"%s\", t1)\n}\n"
  },
  {
    "path": "simple/s1025/testdata/go1.18/CheckRedundantSprintf/LintRedundantSprintf.go",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype MyByte byte\ntype T1 []MyByte\n\nfunc fn() {\n\tvar t1 T1\n\t_ = fmt.Sprintf(\"%s\", t1) //@ diag(`underlying type is a slice of bytes`)\n}\n"
  },
  {
    "path": "simple/s1025/testdata/go1.18/CheckRedundantSprintf/LintRedundantSprintf.go.golden",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype MyByte byte\ntype T1 []MyByte\n\nfunc fn() {\n\tvar t1 T1\n\t_ = string(t1) //@ diag(`underlying type is a slice of bytes`)\n}\n"
  },
  {
    "path": "simple/s1025/testdata/go1.9/CheckRedundantSprintf/LintRedundantSprintf.go",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype T6 string\ntype T9 string\n\ntype MyByte byte\n\ntype Alias = byte\ntype T13 = []byte\ntype T14 = []Alias\n\ntype T15 = string\ntype T16 = T9\ntype T17 = T6\n\nfunc (T6) String() string { return \"\" }\n\nfunc (T9) Format(f fmt.State, c rune) {}\n\nfunc fn() {\n\tvar t12 []Alias\n\tvar t13 T13\n\tvar t14 T14\n\tvar t15 T15\n\tvar t16 T16\n\tvar t17 T17\n\n\t_ = fmt.Sprintf(\"%s\", t12) //@ diag(`underlying type is a slice of bytes`)\n\t_ = fmt.Sprintf(\"%s\", t13) //@ diag(`underlying type is a slice of bytes`)\n\t_ = fmt.Sprintf(\"%s\", t14) //@ diag(`underlying type is a slice of bytes`)\n\t_ = fmt.Sprintf(\"%s\", t15) //@ diag(`is already a string`)\n\t_ = fmt.Sprintf(\"%s\", t17) //@ diag(`should use String() instead of fmt.Sprintf`)\n\n\t// don't simplify types that implement fmt.Formatter\n\t_ = fmt.Sprintf(\"%s\", t16)\n}\n"
  },
  {
    "path": "simple/s1025/testdata/go1.9/CheckRedundantSprintf/LintRedundantSprintf.go.golden",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype T6 string\ntype T9 string\n\ntype MyByte byte\n\ntype Alias = byte\ntype T13 = []byte\ntype T14 = []Alias\n\ntype T15 = string\ntype T16 = T9\ntype T17 = T6\n\nfunc (T6) String() string { return \"\" }\n\nfunc (T9) Format(f fmt.State, c rune) {}\n\nfunc fn() {\n\tvar t12 []Alias\n\tvar t13 T13\n\tvar t14 T14\n\tvar t15 T15\n\tvar t16 T16\n\tvar t17 T17\n\n\t_ = string(t12)  //@ diag(`underlying type is a slice of bytes`)\n\t_ = string(t13)  //@ diag(`underlying type is a slice of bytes`)\n\t_ = string(t14)  //@ diag(`underlying type is a slice of bytes`)\n\t_ = t15          //@ diag(`is already a string`)\n\t_ = t17.String() //@ diag(`should use String() instead of fmt.Sprintf`)\n\n\t// don't simplify types that implement fmt.Formatter\n\t_ = fmt.Sprintf(\"%s\", t16)\n}\n"
  },
  {
    "path": "simple/s1028/s1028.go",
    "content": "package s1028\n\nimport (\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1028\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:   `Simplify error construction with \\'fmt.Errorf\\'`,\n\t\tBefore:  `errors.New(fmt.Sprintf(...))`,\n\t\tAfter:   `fmt.Errorf(...)`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckErrorsNewSprintfQ = pattern.MustParse(`(CallExpr (Symbol \"errors.New\") [(CallExpr (Symbol \"fmt.Sprintf\") args)])`)\n\tcheckErrorsNewSprintfR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident \"fmt\") (Ident \"Errorf\")) args)`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkErrorsNewSprintfQ) {\n\t\tedits := code.EditMatch(pass, node, m, checkErrorsNewSprintfR)\n\t\t// TODO(dh): the suggested fix may leave an unused import behind\n\t\treport.Report(pass, node, \"should use fmt.Errorf(...) instead of errors.New(fmt.Sprintf(...))\",\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(\"Use fmt.Errorf\", edits...)))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1028/s1028_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1028\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1028/testdata/go1.0/CheckErrorsNewSprintf/LintErrorsNewSprintf.go",
    "content": "package pkg\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc fn() {\n\t_ = fmt.Errorf(\"%d\", 0)\n\t_ = errors.New(\"\")\n\t_ = errors.New(fmt.Sprintf(\"%d\", 0)) //@ diag(`should use fmt.Errorf`)\n}\n"
  },
  {
    "path": "simple/s1028/testdata/go1.0/CheckErrorsNewSprintf/LintErrorsNewSprintf.go.golden",
    "content": "package pkg\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc fn() {\n\t_ = fmt.Errorf(\"%d\", 0)\n\t_ = errors.New(\"\")\n\t_ = fmt.Errorf(\"%d\", 0) //@ diag(`should use fmt.Errorf`)\n}\n"
  },
  {
    "path": "simple/s1029/s1029.go",
    "content": "package s1029\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/internal/sharedcheck\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1029\",\n\t\tRun:      sharedcheck.CheckRangeStringRunes,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Range over the string directly`,\n\t\tText: `Ranging over a string will yield byte offsets and runes. If the offset\nisn't used, this is functionally equivalent to converting the string\nto a slice of runes and ranging over that. Ranging directly over the\nstring will be more performant, however, as it avoids allocating a new\nslice, the size of which depends on the length of the string.`,\n\t\tBefore:  `for _, r := range []rune(s) {}`,\n\t\tAfter:   `for _, r := range s {}`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n"
  },
  {
    "path": "simple/s1029/s1029_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1029\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1029/testdata/go1.0/CheckRangeStringRunes/LintRangeStringRunes.go",
    "content": "package pkg\n\ntype String string\n\nfunc fn(s string, s2 String) {\n\tfor _, r := range s {\n\t\tprintln(r)\n\t}\n\n\tfor _, r := range []rune(s) { //@ diag(`should range over string`)\n\t\tprintln(r)\n\t}\n\n\tfor i, r := range []rune(s) {\n\t\tprintln(i)\n\t\tprintln(r)\n\t}\n\n\tx := []rune(s)\n\tfor _, r := range x { //@ diag(`should range over string`)\n\t\tprintln(r)\n\t}\n\n\ty := []rune(s)\n\tfor _, r := range y {\n\t\tprintln(r)\n\t}\n\tprintln(y[0])\n\n\tfor _, r := range []rune(s2) { //@ diag(`should range over string`)\n\t\tprintln(r)\n\t}\n}\n"
  },
  {
    "path": "simple/s1029/testdata/go1.18/CheckRangeStringRunes/generics.go",
    "content": "package pkg\n\nfunc tpfn1[T string](x T) {\n\tfor _, c := range []rune(x) { //@ diag(`should range over string`)\n\t\tprintln(c)\n\t}\n}\n\nfunc tpfn2[T1 string, T2 []rune](x T1) {\n\tfor _, c := range T2(x) { //@ diag(`should range over string`)\n\t\tprintln(c)\n\t}\n}\n"
  },
  {
    "path": "simple/s1030/s1030.go",
    "content": "package s1030\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1030\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Use \\'bytes.Buffer.String\\' or \\'bytes.Buffer.Bytes\\'`,\n\t\tText: `\\'bytes.Buffer\\' has both a \\'String\\' and a \\'Bytes\\' method. It is almost never\nnecessary to use \\'string(buf.Bytes())\\' or \\'[]byte(buf.String())\\' – simply\nuse the other method.\n\nThe only exception to this are map lookups. Due to a compiler optimization,\n\\'m[string(buf.Bytes())]\\' is more efficient than \\'m[buf.String()]\\'.\n`,\n\t\tSince:   \"2017.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckBytesBufferConversionsQ  = pattern.MustParse(`(CallExpr _ [(CallExpr sel@(SelectorExpr recv _) [])])`)\n\tcheckBytesBufferConversionsRs = pattern.MustParse(`(CallExpr (SelectorExpr recv (Ident \"String\")) [])`)\n\tcheckBytesBufferConversionsRb = pattern.MustParse(`(CallExpr (SelectorExpr recv (Ident \"Bytes\")) [])`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tif pass.Pkg.Path() == \"bytes\" || pass.Pkg.Path() == \"bytes_test\" {\n\t\t// The bytes package can use itself however it wants\n\t\treturn nil, nil\n\t}\n\tfn := func(c inspector.Cursor) {\n\t\tnode := c.Node()\n\t\tm, ok := code.Match(pass, checkBytesBufferConversionsQ, node)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tcall := node.(*ast.CallExpr)\n\t\tsel := m.State[\"sel\"].(*ast.SelectorExpr)\n\n\t\ttyp := pass.TypesInfo.TypeOf(call.Fun)\n\t\tif types.Unalias(typ) == types.Universe.Lookup(\"string\").Type() && code.IsCallTo(pass, call.Args[0], \"(*bytes.Buffer).Bytes\") {\n\t\t\tif _, ok := c.Parent().Node().(*ast.IndexExpr); ok {\n\t\t\t\t// Don't flag m[string(buf.Bytes())] – thanks to a\n\t\t\t\t// compiler optimization, this is actually faster than\n\t\t\t\t// m[buf.String()]\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\treport.Report(pass, call, fmt.Sprintf(\"should use %v.String() instead of %v\", report.Render(pass, sel.X), report.Render(pass, call)),\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.Fixes(edit.Fix(\"Simplify conversion\", edit.ReplaceWithPattern(pass.Fset, node, checkBytesBufferConversionsRs, m.State))))\n\t\t} else if typ, ok := types.Unalias(typ).(*types.Slice); ok &&\n\t\t\ttypes.Unalias(typ.Elem()) == types.Universe.Lookup(\"byte\").Type() &&\n\t\t\tcode.IsCallTo(pass, call.Args[0], \"(*bytes.Buffer).String\") {\n\t\t\treport.Report(pass, call, fmt.Sprintf(\"should use %v.Bytes() instead of %v\", report.Render(pass, sel.X), report.Render(pass, call)),\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.Fixes(edit.Fix(\"Simplify conversion\", edit.ReplaceWithPattern(pass.Fset, node, checkBytesBufferConversionsRb, m.State))))\n\t\t}\n\n\t}\n\tfor c := range code.Cursor(pass).Preorder((*ast.CallExpr)(nil)) {\n\t\tfn(c)\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1030/s1030_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1030\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1030/testdata/go1.0/CheckBytesBufferConversions/LintBytesBufferConversions.go",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n)\n\nfunc fn() {\n\tbuf := bytes.NewBufferString(\"str\")\n\t_ = string(buf.Bytes())  //@ diag(`should use buf.String() instead of string(buf.Bytes())`)\n\t_ = []byte(buf.String()) //@ diag(`should use buf.Bytes() instead of []byte(buf.String())`)\n\n\tm := map[string]*bytes.Buffer{\"key\": buf}\n\t_ = string(m[\"key\"].Bytes())  //@ diag(`should use m[\"key\"].String() instead of string(m[\"key\"].Bytes())`)\n\t_ = []byte(m[\"key\"].String()) //@ diag(`should use m[\"key\"].Bytes() instead of []byte(m[\"key\"].String())`)\n\n\tvar m2 map[string]int\n\t_ = m2[string(buf.Bytes())] // no warning, this is more efficient than buf.String()\n\n\tstring := func(_ interface{}) interface{} {\n\t\treturn nil\n\t}\n\t_ = string(m[\"key\"].Bytes())\n}\n"
  },
  {
    "path": "simple/s1030/testdata/go1.0/CheckBytesBufferConversions/LintBytesBufferConversions.go.golden",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n)\n\nfunc fn() {\n\tbuf := bytes.NewBufferString(\"str\")\n\t_ = buf.String() //@ diag(`should use buf.String() instead of string(buf.Bytes())`)\n\t_ = buf.Bytes()  //@ diag(`should use buf.Bytes() instead of []byte(buf.String())`)\n\n\tm := map[string]*bytes.Buffer{\"key\": buf}\n\t_ = m[\"key\"].String() //@ diag(`should use m[\"key\"].String() instead of string(m[\"key\"].Bytes())`)\n\t_ = m[\"key\"].Bytes()  //@ diag(`should use m[\"key\"].Bytes() instead of []byte(m[\"key\"].String())`)\n\n\tvar m2 map[string]int\n\t_ = m2[string(buf.Bytes())] // no warning, this is more efficient than buf.String()\n\n\tstring := func(_ interface{}) interface{} {\n\t\treturn nil\n\t}\n\t_ = string(m[\"key\"].Bytes())\n}\n"
  },
  {
    "path": "simple/s1030/testdata/go1.9/CheckBytesBufferConversions/LintBytesBufferConversions.go",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n)\n\ntype AliasByte = byte\ntype AliasSlice = []byte\ntype AliasSlice2 = []AliasByte\n\nfunc fn() {\n\tbuf := bytes.NewBufferString(\"str\")\n\tm := map[string]*bytes.Buffer{\"key\": buf}\n\n\t_ = []AliasByte(m[\"key\"].String()) //@ diag(`should use m[\"key\"].Bytes() instead of []AliasByte(m[\"key\"].String())`)\n\t_ = AliasSlice(m[\"key\"].String())  //@ diag(`should use m[\"key\"].Bytes() instead of AliasSlice(m[\"key\"].String())`)\n\t_ = AliasSlice2(m[\"key\"].String()) //@ diag(`should use m[\"key\"].Bytes() instead of AliasSlice2(m[\"key\"].String())`)\n}\n"
  },
  {
    "path": "simple/s1030/testdata/go1.9/CheckBytesBufferConversions/LintBytesBufferConversions.go.golden",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n)\n\ntype AliasByte = byte\ntype AliasSlice = []byte\ntype AliasSlice2 = []AliasByte\n\nfunc fn() {\n\tbuf := bytes.NewBufferString(\"str\")\n\tm := map[string]*bytes.Buffer{\"key\": buf}\n\n\t_ = m[\"key\"].Bytes() //@ diag(`should use m[\"key\"].Bytes() instead of []AliasByte(m[\"key\"].String())`)\n\t_ = m[\"key\"].Bytes() //@ diag(`should use m[\"key\"].Bytes() instead of AliasSlice(m[\"key\"].String())`)\n\t_ = m[\"key\"].Bytes() //@ diag(`should use m[\"key\"].Bytes() instead of AliasSlice2(m[\"key\"].String())`)\n}\n"
  },
  {
    "path": "simple/s1031/s1031.go",
    "content": "package s1031\n\nimport (\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1031\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Omit redundant nil check around loop`,\n\t\tText: `You can use range on nil slices and maps, the loop will simply never\nexecute. This makes an additional nil check around the loop\nunnecessary.`,\n\t\tBefore: `\nif s != nil {\n    for _, x := range s {\n        ...\n    }\n}`,\n\t\tAfter: `\nfor _, x := range s {\n    ...\n}`,\n\t\tSince: \"2017.1\",\n\t\t// MergeIfAll because x might be a channel under some build tags.\n\t\t// you shouldn't write code like that…\n\t\tMergeIf: lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkNilCheckAroundRangeQ = pattern.MustParse(`\n\t(IfStmt\n\t\tnil\n\t\t(BinaryExpr x@(Object _) \"!=\" (Builtin \"nil\"))\n\t\t[(RangeStmt _ _ _ x _)]\n\t\tnil)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkNilCheckAroundRangeQ) {\n\t\tok := typeutil.All(m.State[\"x\"].(types.Object).Type(), func(term *types.Term) bool {\n\t\t\tswitch term.Type().Underlying().(type) {\n\t\t\tcase *types.Slice, *types.Map:\n\t\t\t\treturn true\n\t\t\tcase *types.TypeParam, *types.Chan, *types.Pointer, *types.Signature:\n\t\t\t\treturn false\n\t\t\tdefault:\n\t\t\t\tlint.ExhaustiveTypeSwitch(term.Type().Underlying())\n\t\t\t\treturn false\n\t\t\t}\n\t\t})\n\t\tif ok {\n\t\t\treport.Report(pass, node, \"unnecessary nil check around range\", report.ShortRange(), report.FilterGenerated())\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1031/s1031_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1031\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1031/testdata/go1.0/CheckNilCheckAroundRange/LintNilCheckAroundRange.go",
    "content": "package pkg\n\nimport \"fmt\"\n\nfunc main() {\n\tstr := []string{}\n\n\t// range outside nil check should not match\n\tfor _, s := range str {\n\t\ts = s + \"B\"\n\t}\n\n\t// body with multiple statements should not match\n\tif str != nil {\n\t\tstr = append(str, \"C\")\n\t\tfor _, s := range str {\n\t\t\ts = s + \"D\"\n\t\t}\n\t}\n\n\tif str != nil { //@ diag(`unnecessary nil check around range`)\n\t\tfor _, s := range str {\n\t\t\ts = s + \"A\"\n\t\t}\n\t}\n\n\tvar nilMap map[string]int\n\tif nilMap != nil { //@ diag(`unnecessary nil check around range`)\n\t\tfor key, value := range nilMap {\n\t\t\tnilMap[key] = value + 1\n\t\t}\n\t}\n\n\t// range over channel can have nil check, as it is required to avoid blocking\n\tvar nilChan chan int\n\tif nilChan != nil {\n\t\tfor v := range nilChan {\n\t\t\tfmt.Println(v)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "simple/s1031/testdata/go1.18/CheckNilCheckAroundRange/CheckNilCheckAroundRange.go",
    "content": "package pkg\n\nfunc _[T int | string](x []T) {\n\tif x != nil { //@ diag(`unnecessary nil check around range`)\n\t\tfor range x {\n\t\t}\n\t}\n}\n\nfunc _[T int | string, S []T](x S) {\n\tif x != nil { //@ diag(`unnecessary nil check around range`)\n\t\tfor range x {\n\t\t}\n\t}\n}\n\nfunc _[T []string](x T) {\n\tif x != nil { //@ diag(`unnecessary nil check around range`)\n\t\tfor range x {\n\t\t}\n\t}\n}\n\nfunc _[T chan int](x T) {\n\tif x != nil {\n\t\tfor range x {\n\t\t}\n\t}\n}\n\nfunc _[T any, S chan T](x S) {\n\tif x != nil {\n\t\tfor range x {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "simple/s1032/s1032.go",
    "content": "package s1032\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\t\"sort\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1032\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Use \\'sort.Ints(x)\\', \\'sort.Float64s(x)\\', and \\'sort.Strings(x)\\'`,\n\t\tText: `The \\'sort.Ints\\', \\'sort.Float64s\\' and \\'sort.Strings\\' functions are easier to\nread than \\'sort.Sort(sort.IntSlice(x))\\', \\'sort.Sort(sort.Float64Slice(x))\\'\nand \\'sort.Sort(sort.StringSlice(x))\\'.`,\n\t\tBefore:  `sort.Sort(sort.StringSlice(x))`,\n\t\tAfter:   `sort.Strings(x)`,\n\t\tSince:   \"2019.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc isPermissibleSort(pass *analysis.Pass, node ast.Node) bool {\n\tcall := node.(*ast.CallExpr)\n\ttypeconv, ok := call.Args[0].(*ast.CallExpr)\n\tif !ok {\n\t\treturn true\n\t}\n\n\tsel, ok := typeconv.Fun.(*ast.SelectorExpr)\n\tif !ok {\n\t\treturn true\n\t}\n\tname := code.SelectorName(pass, sel)\n\tswitch name {\n\tcase \"sort.IntSlice\", \"sort.Float64Slice\", \"sort.StringSlice\":\n\tdefault:\n\t\treturn true\n\t}\n\n\treturn false\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\ttype Error struct {\n\t\tnode ast.Node\n\t\tmsg  string\n\t}\n\tvar allErrors []Error\n\tfn := func(node ast.Node) {\n\t\tvar body *ast.BlockStmt\n\t\tswitch node := node.(type) {\n\t\tcase *ast.FuncLit:\n\t\t\tbody = node.Body\n\t\tcase *ast.FuncDecl:\n\t\t\tbody = node.Body\n\t\tdefault:\n\t\t\tlint.ExhaustiveTypeSwitch(node)\n\t\t}\n\t\tif body == nil {\n\t\t\treturn\n\t\t}\n\n\t\tvar errors []Error\n\t\tpermissible := false\n\t\tfnSorts := func(node ast.Node) bool {\n\t\t\tif permissible {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif !code.IsCallTo(pass, node, \"sort.Sort\") {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif isPermissibleSort(pass, node) {\n\t\t\t\tpermissible = true\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tcall := node.(*ast.CallExpr)\n\t\t\t// isPermissibleSort guarantees that this type assertion will succeed\n\t\t\ttypeconv := call.Args[knowledge.Arg(\"sort.Sort.data\")].(*ast.CallExpr)\n\t\t\tsel := typeconv.Fun.(*ast.SelectorExpr)\n\t\t\tname := code.SelectorName(pass, sel)\n\n\t\t\tswitch name {\n\t\t\tcase \"sort.IntSlice\":\n\t\t\t\terrors = append(errors, Error{node, \"should use sort.Ints(...) instead of sort.Sort(sort.IntSlice(...))\"})\n\t\t\tcase \"sort.Float64Slice\":\n\t\t\t\terrors = append(errors, Error{node, \"should use sort.Float64s(...) instead of sort.Sort(sort.Float64Slice(...))\"})\n\t\t\tcase \"sort.StringSlice\":\n\t\t\t\terrors = append(errors, Error{node, \"should use sort.Strings(...) instead of sort.Sort(sort.StringSlice(...))\"})\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t\tast.Inspect(body, fnSorts)\n\n\t\tif permissible {\n\t\t\treturn\n\t\t}\n\t\tallErrors = append(allErrors, errors...)\n\t}\n\tcode.Preorder(pass, fn, (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil))\n\tsort.Slice(allErrors, func(i, j int) bool {\n\t\treturn allErrors[i].node.Pos() < allErrors[j].node.Pos()\n\t})\n\tvar prev token.Pos\n\tfor _, err := range allErrors {\n\t\tif err.node.Pos() == prev {\n\t\t\tcontinue\n\t\t}\n\t\tprev = err.node.Pos()\n\t\treport.Report(pass, err.node, err.msg, report.FilterGenerated())\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1032/s1032_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1032\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1032/testdata/go1.0/CheckSortHelpers/LintSortHelpers.go",
    "content": "package pkg\n\nimport \"sort\"\n\ntype MyIntSlice []int\n\nfunc (s MyIntSlice) Len() int           { return 0 }\nfunc (s MyIntSlice) Less(i, j int) bool { return true }\nfunc (s MyIntSlice) Swap(i, j int)      {}\n\nfunc fn1() {\n\tvar a []int\n\tsort.Sort(sort.IntSlice(a)) //@ diag(`sort.Ints`)\n}\n\nfunc fn2() {\n\tvar b []float64\n\tsort.Sort(sort.Float64Slice(b)) //@ diag(`sort.Float64s`)\n}\n\nfunc fn3() {\n\tvar c []string\n\tsort.Sort(sort.StringSlice(c)) //@ diag(`sort.Strings`)\n}\n\nfunc fn4() {\n\tvar a []int\n\tsort.Sort(MyIntSlice(a))\n}\n\nfunc fn5() {\n\tvar d MyIntSlice\n\tsort.Sort(d)\n}\n\nfunc fn6() {\n\tvar e sort.Interface\n\tsort.Sort(e)\n}\n\nfunc fn7() {\n\t// Don't recommend sort.Ints when there was another legitimate\n\t// sort.Sort call already\n\tvar a []int\n\tvar e sort.Interface\n\tsort.Sort(e)\n\tsort.Sort(sort.IntSlice(a))\n}\n\nfunc fn8() {\n\tvar a []int\n\tsort.Sort(sort.IntSlice(a)) //@ diag(`sort.Ints`)\n\tsort.Sort(sort.IntSlice(a)) //@ diag(`sort.Ints`)\n}\n\nfunc fn9() {\n\tfunc() {\n\t\tvar a []int\n\t\tsort.Sort(sort.IntSlice(a)) //@ diag(`sort.Ints`)\n\t}()\n}\n\nfunc fn10() {\n\tvar a MyIntSlice\n\tsort.Sort(sort.IntSlice(a)) //@ diag(`sort.Ints`)\n}\n"
  },
  {
    "path": "simple/s1033/s1033.go",
    "content": "package s1033\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1033\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:   `Unnecessary guard around call to \\\"delete\\\"`,\n\t\tText:    `Calling \\'delete\\' on a nil map is a no-op.`,\n\t\tSince:   \"2019.2\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkGuardedDeleteQ = pattern.MustParse(`\n\t(IfStmt\n\t\t(AssignStmt\n\t\t\t[(Ident \"_\") ok@(Ident _)]\n\t\t\t\":=\"\n\t\t\t(IndexExpr m key))\n\t\tok\n\t\t[call@(CallExpr (Builtin \"delete\") [m key])]\n\t\tnil)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkGuardedDeleteQ) {\n\t\treport.Report(pass, node, \"unnecessary guard around call to delete\",\n\t\t\treport.ShortRange(),\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(\"Remove guard\", edit.ReplaceWithNode(pass.Fset, node, m.State[\"call\"].(ast.Node)))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1033/s1033_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1033\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1033/testdata/go1.0/CheckGuardedDelete/LintGuardedDelete.go",
    "content": "// Package pkg ...\npackage pkg\n\nfunc fn(m map[int]int) {\n\tif _, ok := m[0]; ok { //@ diag(`unnecessary guard`)\n\t\tdelete(m, 0)\n\t}\n\tif _, ok := m[0]; !ok {\n\t\tdelete(m, 0)\n\t}\n\tif _, ok := m[0]; ok {\n\t\tprintln(\"deleting\")\n\t\tdelete(m, 0)\n\t}\n\tif v, ok := m[0]; ok && v > 0 {\n\t\tdelete(m, 0)\n\t}\n\n\tvar key int\n\tif _, ok := m[key]; ok { //@ diag(`unnecessary guard`)\n\t\tdelete(m, key)\n\t}\n\tif _, ok := m[key]; ok {\n\t\tdelete(m, 0)\n\t}\n\tif _, ok := m[key]; ok {\n\t\tdelete(m, key)\n\t} else {\n\t\tprintln(\"not deleted\")\n\t}\n\n\tvar ok bool\n\tif _, ok = m[key]; ok {\n\t\tdelete(m, 0)\n\t}\n\tif ok {\n\t\tprintln(\"deleted\")\n\t}\n\n\tdelete := func(a, b interface{}) {}\n\tif _, ok := m[0]; ok {\n\t\tdelete(m, 0)\n\t}\n}\n"
  },
  {
    "path": "simple/s1033/testdata/go1.0/CheckGuardedDelete/LintGuardedDelete.go.golden",
    "content": "// Package pkg ...\npackage pkg\n\nfunc fn(m map[int]int) {\n\tdelete(m, 0)\n\tif _, ok := m[0]; !ok {\n\t\tdelete(m, 0)\n\t}\n\tif _, ok := m[0]; ok {\n\t\tprintln(\"deleting\")\n\t\tdelete(m, 0)\n\t}\n\tif v, ok := m[0]; ok && v > 0 {\n\t\tdelete(m, 0)\n\t}\n\n\tvar key int\n\tdelete(m, key)\n\tif _, ok := m[key]; ok {\n\t\tdelete(m, 0)\n\t}\n\tif _, ok := m[key]; ok {\n\t\tdelete(m, key)\n\t} else {\n\t\tprintln(\"not deleted\")\n\t}\n\n\tvar ok bool\n\tif _, ok = m[key]; ok {\n\t\tdelete(m, 0)\n\t}\n\tif ok {\n\t\tprintln(\"deleted\")\n\t}\n\n\tdelete := func(a, b interface{}) {}\n\tif _, ok := m[0]; ok {\n\t\tdelete(m, 0)\n\t}\n}\n"
  },
  {
    "path": "simple/s1034/s1034.go",
    "content": "package s1034\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1034\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:   `Use result of type assertion to simplify cases`,\n\t\tSince:   \"2019.2\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckSimplifyTypeSwitchQ = pattern.MustParse(`\n\t\t(TypeSwitchStmt\n\t\t\tnil\n\t\t\texpr@(TypeAssertExpr ident@(Ident _) _)\n\t\t\tbody)`)\n\tcheckSimplifyTypeSwitchR = pattern.MustParse(`(AssignStmt ident \":=\" expr)`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkSimplifyTypeSwitchQ) {\n\t\tstmt := node.(*ast.TypeSwitchStmt)\n\t\texpr := m.State[\"expr\"].(ast.Node)\n\t\tident := m.State[\"ident\"].(*ast.Ident)\n\n\t\tx := pass.TypesInfo.ObjectOf(ident)\n\t\tvar allOffenders []*ast.TypeAssertExpr\n\t\tcanSuggestFix := true\n\t\tfor _, clause := range stmt.Body.List {\n\t\t\tclause := clause.(*ast.CaseClause)\n\t\t\tif len(clause.List) != 1 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\thasUnrelatedAssertion := false\n\t\t\tvar offenders []*ast.TypeAssertExpr\n\t\t\tast.Inspect(clause, func(node ast.Node) bool {\n\t\t\t\tassert2, ok := node.(*ast.TypeAssertExpr)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tident, ok := assert2.X.(*ast.Ident)\n\t\t\t\tif !ok {\n\t\t\t\t\thasUnrelatedAssertion = true\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif pass.TypesInfo.ObjectOf(ident) != x {\n\t\t\t\t\thasUnrelatedAssertion = true\n\t\t\t\t\treturn false\n\t\t\t\t}\n\n\t\t\t\tif !types.Identical(pass.TypesInfo.TypeOf(clause.List[0]), pass.TypesInfo.TypeOf(assert2.Type)) {\n\t\t\t\t\thasUnrelatedAssertion = true\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\toffenders = append(offenders, assert2)\n\t\t\t\treturn true\n\t\t\t})\n\t\t\tif !hasUnrelatedAssertion {\n\t\t\t\t// don't flag cases that have other type assertions\n\t\t\t\t// unrelated to the one in the case clause. often\n\t\t\t\t// times, this is done for symmetry, when two\n\t\t\t\t// different values have to be asserted to the same\n\t\t\t\t// type.\n\t\t\t\tallOffenders = append(allOffenders, offenders...)\n\t\t\t}\n\t\t\tcanSuggestFix = canSuggestFix && !hasUnrelatedAssertion\n\t\t}\n\t\tif len(allOffenders) != 0 {\n\t\t\tvar opts []report.Option\n\t\t\tfor _, offender := range allOffenders {\n\t\t\t\topts = append(opts, report.Related(offender, \"could eliminate this type assertion\"))\n\t\t\t}\n\t\t\topts = append(opts, report.FilterGenerated())\n\n\t\t\tmsg := fmt.Sprintf(\"assigning the result of this type assertion to a variable (switch %s := %s.(type)) could eliminate type assertions in switch cases\",\n\t\t\t\treport.Render(pass, ident), report.Render(pass, ident))\n\t\t\tif canSuggestFix {\n\t\t\t\tvar edits []analysis.TextEdit\n\t\t\t\tedits = append(edits, edit.ReplaceWithPattern(pass.Fset, expr, checkSimplifyTypeSwitchR, m.State))\n\t\t\t\tfor _, offender := range allOffenders {\n\t\t\t\t\tedits = append(edits, edit.ReplaceWithNode(pass.Fset, offender, offender.X))\n\t\t\t\t}\n\t\t\t\topts = append(opts, report.Fixes(edit.Fix(\"Simplify type switch\", edits...)))\n\t\t\t\treport.Report(pass, expr, msg, opts...)\n\t\t\t} else {\n\t\t\t\treport.Report(pass, expr, msg, opts...)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1034/s1034_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1034\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1034/testdata/go1.0/CheckSimplifyTypeSwitch/LintSimplifyTypeSwitch.go",
    "content": "package pkg\n\nimport \"fmt\"\n\nfunc gen() interface{} { return nil }\n\nfunc fn(x, y interface{}) {\n\tswitch z := x.(type) {\n\tcase int:\n\t\t_ = z\n\t\tfmt.Println(x.(int))\n\t}\n\tswitch x.(type) {\n\tcase int:\n\t\tfmt.Println(x.(int), y.(int))\n\t}\n\tswitch x.(type) { //@ diag(`assigning the result of this type assertion`)\n\tcase int:\n\t\tfmt.Println(x.(int))\n\t}\n\tswitch x.(type) {\n\tcase int:\n\t\tfmt.Println(x.(string))\n\t}\n\tswitch x.(type) {\n\tcase int:\n\t\tfmt.Println(y.(int))\n\t}\n\tswitch (gen()).(type) {\n\tcase int:\n\t\tfmt.Println((gen()).(int))\n\t}\n}\n"
  },
  {
    "path": "simple/s1034/testdata/go1.0/CheckSimplifyTypeSwitch/LintSimplifyTypeSwitch.go.golden",
    "content": "package pkg\n\nimport \"fmt\"\n\nfunc gen() interface{} { return nil }\n\nfunc fn(x, y interface{}) {\n\tswitch z := x.(type) {\n\tcase int:\n\t\t_ = z\n\t\tfmt.Println(x.(int))\n\t}\n\tswitch x.(type) {\n\tcase int:\n\t\tfmt.Println(x.(int), y.(int))\n\t}\n\tswitch x := x.(type) { //@ diag(`assigning the result of this type assertion`)\n\tcase int:\n\t\tfmt.Println(x)\n\t}\n\tswitch x.(type) {\n\tcase int:\n\t\tfmt.Println(x.(string))\n\t}\n\tswitch x.(type) {\n\tcase int:\n\t\tfmt.Println(y.(int))\n\t}\n\tswitch (gen()).(type) {\n\tcase int:\n\t\tfmt.Println((gen()).(int))\n\t}\n}\n"
  },
  {
    "path": "simple/s1035/s1035.go",
    "content": "package s1035\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1035\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Redundant call to \\'net/http.CanonicalHeaderKey\\' in method call on \\'net/http.Header\\'`,\n\t\tText: `\nThe methods on \\'net/http.Header\\', namely \\'Add\\', \\'Del\\', \\'Get\\'\nand \\'Set\\', already canonicalize the given header name.`,\n\t\tSince:   \"2020.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar query = pattern.MustParse(`\n\t(CallExpr\n\t\t(Symbol\n\t\t\tcallName@(Or\n\t\t\t\t\"(net/http.Header).Add\"\n\t\t\t\t\"(net/http.Header).Del\"\n\t\t\t\t\"(net/http.Header).Get\"\n\t\t\t\t\"(net/http.Header).Set\"))\n\t\targ@(CallExpr (Symbol \"net/http.CanonicalHeaderKey\") _):_)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, m := range code.Matches(pass, query) {\n\t\targ := m.State[\"arg\"].(*ast.CallExpr)\n\t\treport.Report(pass, m.State[\"arg\"].(ast.Expr),\n\t\t\tfmt.Sprintf(\"calling net/http.CanonicalHeaderKey on the 'key' argument of %s is redundant\", m.State[\"callName\"].(string)),\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(\"Remove call to CanonicalHeaderKey\", edit.ReplaceWithNode(pass.Fset, arg, arg.Args[0]))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1035/s1035_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1035\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1035/testdata/go1.0/CheckRedundantCanonicalHeaderKey/LintRedundantCanonicalHeaderKey.go",
    "content": "package pkg\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc fn1() {\n\tvar headers http.Header\n\n\t// Matches\n\theaders.Add(http.CanonicalHeaderKey(\"test\"), \"test\") //@ diag(`calling net/http.CanonicalHeaderKey on the 'key' argument of`)\n\theaders.Del(http.CanonicalHeaderKey(\"test\"))         //@ diag(`calling net/http.CanonicalHeaderKey on the 'key' argument of`)\n\theaders.Get(http.CanonicalHeaderKey(\"test\"))         //@ diag(`calling net/http.CanonicalHeaderKey on the 'key' argument of`)\n\theaders.Set(http.CanonicalHeaderKey(\"test\"), \"test\") //@ diag(`calling net/http.CanonicalHeaderKey on the 'key' argument of`)\n\n\t// Non-matches\n\theaders.Add(\"test\", \"test\")\n\theaders.Del(\"test\")\n\theaders.Get(\"test\")\n\theaders.Set(\"test\", \"test\")\n\n\theaders.Add(\"test\", http.CanonicalHeaderKey(\"test\"))\n\theaders.Set(\"test\", http.CanonicalHeaderKey(\"test\"))\n\n\theaders.Add(http.CanonicalHeaderKey(\"test\")+\"1\", \"test\")\n\theaders.Del(http.CanonicalHeaderKey(\"test\") + \"1\")\n\theaders.Get(http.CanonicalHeaderKey(\"test\") + \"1\")\n\theaders.Set(http.CanonicalHeaderKey(\"test\")+\"1\", \"test\")\n\n\theaders.Add(strings.ToUpper(http.CanonicalHeaderKey(\"test\")), \"test\")\n\theaders.Del(strings.ToUpper(http.CanonicalHeaderKey(\"test\")))\n\theaders.Get(strings.ToUpper(http.CanonicalHeaderKey(\"test\")))\n\theaders.Set(strings.ToUpper(http.CanonicalHeaderKey(\"test\")), \"test\")\n}\n"
  },
  {
    "path": "simple/s1035/testdata/go1.0/CheckRedundantCanonicalHeaderKey/LintRedundantCanonicalHeaderKey.go.golden",
    "content": "package pkg\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc fn1() {\n\tvar headers http.Header\n\n\t// Matches\n\theaders.Add(\"test\", \"test\") //@ diag(`calling net/http.CanonicalHeaderKey on the 'key' argument of`)\n\theaders.Del(\"test\")         //@ diag(`calling net/http.CanonicalHeaderKey on the 'key' argument of`)\n\theaders.Get(\"test\")         //@ diag(`calling net/http.CanonicalHeaderKey on the 'key' argument of`)\n\theaders.Set(\"test\", \"test\") //@ diag(`calling net/http.CanonicalHeaderKey on the 'key' argument of`)\n\n\t// Non-matches\n\theaders.Add(\"test\", \"test\")\n\theaders.Del(\"test\")\n\theaders.Get(\"test\")\n\theaders.Set(\"test\", \"test\")\n\n\theaders.Add(\"test\", http.CanonicalHeaderKey(\"test\"))\n\theaders.Set(\"test\", http.CanonicalHeaderKey(\"test\"))\n\n\theaders.Add(http.CanonicalHeaderKey(\"test\")+\"1\", \"test\")\n\theaders.Del(http.CanonicalHeaderKey(\"test\") + \"1\")\n\theaders.Get(http.CanonicalHeaderKey(\"test\") + \"1\")\n\theaders.Set(http.CanonicalHeaderKey(\"test\")+\"1\", \"test\")\n\n\theaders.Add(strings.ToUpper(http.CanonicalHeaderKey(\"test\")), \"test\")\n\theaders.Del(strings.ToUpper(http.CanonicalHeaderKey(\"test\")))\n\theaders.Get(strings.ToUpper(http.CanonicalHeaderKey(\"test\")))\n\theaders.Set(strings.ToUpper(http.CanonicalHeaderKey(\"test\")), \"test\")\n}\n"
  },
  {
    "path": "simple/s1036/s1036.go",
    "content": "package s1036\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1036\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Unnecessary guard around map access`,\n\n\t\tText: `\nWhen accessing a map key that doesn't exist yet, one receives a zero\nvalue. Often, the zero value is a suitable value, for example when\nusing append or doing integer math.\n\nThe following\n\n    if _, ok := m[\"foo\"]; ok {\n        m[\"foo\"] = append(m[\"foo\"], \"bar\")\n    } else {\n        m[\"foo\"] = []string{\"bar\"}\n    }\n\ncan be simplified to\n\n    m[\"foo\"] = append(m[\"foo\"], \"bar\")\n\nand\n\n    if _, ok := m2[\"k\"]; ok {\n        m2[\"k\"] += 4\n    } else {\n        m2[\"k\"] = 4\n    }\n\ncan be simplified to\n\n    m[\"k\"] += 4\n`,\n\t\tSince:   \"2020.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkUnnecessaryGuardQ = pattern.MustParse(`\n\t(Or\n\t\t(IfStmt\n\t\t\t(AssignStmt [(Ident \"_\") ok@(Ident _)] \":=\" indexexpr@(IndexExpr _ _))\n\t\t\tok\n\t\t\tset@(AssignStmt indexexpr \"=\" (CallExpr (Builtin \"append\") indexexpr:values))\n\t\t\t(AssignStmt indexexpr \"=\" (CompositeLit _ values)))\n\t\t(IfStmt\n\t\t\t(AssignStmt [(Ident \"_\") ok] \":=\" indexexpr@(IndexExpr _ _))\n\t\t\tok\n\t\t\tset@(AssignStmt indexexpr \"+=\" value)\n\t\t\t(AssignStmt indexexpr \"=\" value))\n\t\t(IfStmt\n\t\t\t(AssignStmt [(Ident \"_\") ok] \":=\" indexexpr@(IndexExpr _ _))\n\t\t\tok\n\t\t\tset@(IncDecStmt indexexpr \"++\")\n\t\t\t(AssignStmt indexexpr \"=\" (IntegerLiteral \"1\"))))`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkUnnecessaryGuardQ) {\n\t\tif code.MayHaveSideEffects(pass, m.State[\"indexexpr\"].(ast.Expr), nil) {\n\t\t\tcontinue\n\t\t}\n\t\treport.Report(pass, node, \"unnecessary guard around map access\",\n\t\t\treport.ShortRange(),\n\t\t\treport.Fixes(edit.Fix(\"Simplify map access\", edit.ReplaceWithNode(pass.Fset, node, m.State[\"set\"].(ast.Node)))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1036/s1036_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1036\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1036/testdata/go1.0/CheckUnnecessaryGuard/LintUnnecessaryGuard.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar m = map[string][]string{}\n\n\tif _, ok := m[\"k1\"]; ok { //@ diag(`unnecessary guard around map access`)\n\t\tm[\"k1\"] = append(m[\"k1\"], \"v1\", \"v2\")\n\t} else {\n\t\tm[\"k1\"] = []string{\"v1\", \"v2\"}\n\t}\n\n\tif _, ok := m[\"k1\"]; ok {\n\t\tm[\"k1\"] = append(m[\"k1\"], \"v1\", \"v2\")\n\t} else {\n\t\tm[\"k1\"] = []string{\"v1\"}\n\t}\n\n\tif _, ok := m[\"k1\"]; ok {\n\t\tm[\"k2\"] = append(m[\"k2\"], \"v1\")\n\t} else {\n\t\tm[\"k1\"] = []string{\"v1\"}\n\t}\n\n\tk1 := \"key\"\n\tif _, ok := m[k1]; ok { //@ diag(`unnecessary guard around map access`)\n\t\tm[k1] = append(m[k1], \"v1\", \"v2\")\n\t} else {\n\t\tm[k1] = []string{\"v1\", \"v2\"}\n\t}\n\n\t// ellipsis is not currently supported\n\tv := []string{\"v1\", \"v2\"}\n\tif _, ok := m[\"k1\"]; ok {\n\t\tm[\"k1\"] = append(m[\"k1\"], v...)\n\t} else {\n\t\tm[\"k1\"] = v\n\t}\n\n\tvar m2 map[string]int\n\tif _, ok := m2[\"k\"]; ok { //@ diag(`unnecessary guard around map access`)\n\t\tm2[\"k\"] += 4\n\t} else {\n\t\tm2[\"k\"] = 4\n\t}\n\n\tif _, ok := m2[\"k\"]; ok {\n\t\tm2[\"k\"] += 4\n\t} else {\n\t\tm2[\"k\"] = 3\n\t}\n\n\tif _, ok := m2[\"k\"]; ok { //@ diag(`unnecessary guard around map access`)\n\t\tm2[\"k\"]++\n\t} else {\n\t\tm2[\"k\"] = 1\n\t}\n\n\tif _, ok := m2[\"k\"]; ok {\n\t\tm2[\"k\"] -= 1\n\t} else {\n\t\tm2[\"k\"] = 1\n\t}\n}\n\n// this used to cause a panic in the pattern package\nfunc fn2() {\n\tvar obj interface{}\n\n\tif _, ok := obj.(map[string]interface{})[\"items\"]; ok {\n\t\tobj.(map[string]interface{})[\"version\"] = 1\n\t}\n}\n"
  },
  {
    "path": "simple/s1036/testdata/go1.0/CheckUnnecessaryGuard/LintUnnecessaryGuard.go.golden",
    "content": "package pkg\n\nfunc fn() {\n\tvar m = map[string][]string{}\n\n\tm[\"k1\"] = append(m[\"k1\"], \"v1\", \"v2\")\n\n\tif _, ok := m[\"k1\"]; ok {\n\t\tm[\"k1\"] = append(m[\"k1\"], \"v1\", \"v2\")\n\t} else {\n\t\tm[\"k1\"] = []string{\"v1\"}\n\t}\n\n\tif _, ok := m[\"k1\"]; ok {\n\t\tm[\"k2\"] = append(m[\"k2\"], \"v1\")\n\t} else {\n\t\tm[\"k1\"] = []string{\"v1\"}\n\t}\n\n\tk1 := \"key\"\n\tm[k1] = append(m[k1], \"v1\", \"v2\")\n\n\t// ellipsis is not currently supported\n\tv := []string{\"v1\", \"v2\"}\n\tif _, ok := m[\"k1\"]; ok {\n\t\tm[\"k1\"] = append(m[\"k1\"], v...)\n\t} else {\n\t\tm[\"k1\"] = v\n\t}\n\n\tvar m2 map[string]int\n\tm2[\"k\"] += 4\n\n\tif _, ok := m2[\"k\"]; ok {\n\t\tm2[\"k\"] += 4\n\t} else {\n\t\tm2[\"k\"] = 3\n\t}\n\n\tm2[\"k\"]++\n\n\tif _, ok := m2[\"k\"]; ok {\n\t\tm2[\"k\"] -= 1\n\t} else {\n\t\tm2[\"k\"] = 1\n\t}\n}\n\n// this used to cause a panic in the pattern package\nfunc fn2() {\n\tvar obj interface{}\n\n\tif _, ok := obj.(map[string]interface{})[\"items\"]; ok {\n\t\tobj.(map[string]interface{})[\"version\"] = 1\n\t}\n}\n"
  },
  {
    "path": "simple/s1037/s1037.go",
    "content": "package s1037\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1037\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Elaborate way of sleeping`,\n\t\tText: `Using a select statement with a single case receiving\nfrom the result of \\'time.After\\' is a very elaborate way of sleeping that\ncan much simpler be expressed with a simple call to time.Sleep.`,\n\t\tSince:   \"2020.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckElaborateSleepQ = pattern.MustParse(`(SelectStmt (CommClause (UnaryExpr \"<-\" (CallExpr (Symbol \"time.After\") [arg])) body))`)\n\tcheckElaborateSleepR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident \"time\") (Ident \"Sleep\")) [arg])`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkElaborateSleepQ) {\n\t\tif body, ok := m.State[\"body\"].([]ast.Stmt); ok && len(body) == 0 {\n\t\t\treport.Report(pass, node, \"should use time.Sleep instead of elaborate way of sleeping\",\n\t\t\t\treport.ShortRange(),\n\t\t\t\treport.FilterGenerated(),\n\t\t\t\treport.Fixes(edit.Fix(\"Use time.Sleep\", edit.ReplaceWithPattern(pass.Fset, node, checkElaborateSleepR, m.State))))\n\t\t} else {\n\t\t\t// TODO(dh): we could make a suggested fix if the body\n\t\t\t// doesn't declare or shadow any identifiers\n\t\t\treport.Report(pass, node, \"should use time.Sleep instead of elaborate way of sleeping\",\n\t\t\t\treport.ShortRange(),\n\t\t\t\treport.FilterGenerated())\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1037/s1037_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1037\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1037/testdata/go1.0/CheckElaborateSleep/LintElaborateSleep.go",
    "content": "package pkg\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc fn() {\n\ttime.Sleep(0)\n\n\tselect { //@ diag(`should use time.Sleep`)\n\tcase <-time.After(0):\n\t}\n\n\tselect { //@ diag(`should use time.Sleep`)\n\tcase <-time.After(0):\n\t\tfmt.Println(\"yay\")\n\t}\n\n\tconst d = 0\n\tselect { //@ diag(`should use time.Sleep`)\n\tcase <-time.After(d):\n\t}\n\n\tvar ch chan int\n\tselect {\n\tcase <-time.After(0):\n\tcase <-ch:\n\t}\n}\n"
  },
  {
    "path": "simple/s1037/testdata/go1.0/CheckElaborateSleep/LintElaborateSleep.go.golden",
    "content": "package pkg\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc fn() {\n\ttime.Sleep(0)\n\n\ttime.Sleep(0)\n\n\tselect { //@ diag(`should use time.Sleep`)\n\tcase <-time.After(0):\n\t\tfmt.Println(\"yay\")\n\t}\n\n\tconst d = 0\n\ttime.Sleep(d)\n\n\tvar ch chan int\n\tselect {\n\tcase <-time.After(0):\n\tcase <-ch:\n\t}\n}\n"
  },
  {
    "path": "simple/s1038/s1038.go",
    "content": "package s1038\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1038\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:   \"Unnecessarily complex way of printing formatted string\",\n\t\tText:    `Instead of using \\'fmt.Print(fmt.Sprintf(...))\\', one can use \\'fmt.Printf(...)\\'.`,\n\t\tSince:   \"2020.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckPrintSprintQ = pattern.MustParse(`\n\t\t(Or\n\t\t\t(CallExpr\n\t\t\t\tfn@(Or\n\t\t\t\t\t(Symbol \"fmt.Print\")\n\t\t\t\t\t(Symbol \"fmt.Sprint\")\n\t\t\t\t\t(Symbol \"fmt.Println\")\n\t\t\t\t\t(Symbol \"fmt.Sprintln\"))\n\t\t\t\t[(CallExpr (Symbol \"fmt.Sprintf\") f:_)])\n\t\t\t(CallExpr\n\t\t\t\tfn@(Or\n\t\t\t\t\t(Symbol \"fmt.Fprint\")\n\t\t\t\t\t(Symbol \"fmt.Fprintln\"))\n\t\t\t\t[_ (CallExpr (Symbol \"fmt.Sprintf\") f:_)]))`)\n\n\tcheckTestingErrorSprintfQ = pattern.MustParse(`\n\t\t(CallExpr\n\t\t\tsel@(SelectorExpr\n\t\t\t\trecv\n\t\t\t\t(Ident\n\t\t\t\t\tname@(Or\n\t\t\t\t\t\t\"Error\"\n\t\t\t\t\t\t\"Fatal\"\n\t\t\t\t\t\t\"Fatalln\"\n\t\t\t\t\t\t\"Log\"\n\t\t\t\t\t\t\"Panic\"\n\t\t\t\t\t\t\"Panicln\"\n\t\t\t\t\t\t\"Print\"\n\t\t\t\t\t\t\"Println\"\n\t\t\t\t\t\t\"Skip\")))\n\t\t\t[(CallExpr (Symbol \"fmt.Sprintf\") args)])`)\n\n\tcheckLogSprintfQ = pattern.MustParse(`\n\t\t(CallExpr\n\t\t\t(Symbol\n\t\t\t\t(Or\n\t\t\t\t\t\"log.Fatal\"\n\t\t\t\t\t\"log.Fatalln\"\n\t\t\t\t\t\"log.Panic\"\n\t\t\t\t\t\"log.Panicln\"\n\t\t\t\t\t\"log.Print\"\n\t\t\t\t\t\"log.Println\"))\n\t\t\t[(CallExpr (Symbol \"fmt.Sprintf\") args)])`)\n\n\tcheckSprintfMapping = map[string]struct {\n\t\trecv        string\n\t\talternative string\n\t}{\n\t\t\"(*testing.common).Error\": {\"(*testing.common)\", \"Errorf\"},\n\t\t\"(testing.TB).Error\":      {\"(testing.TB)\", \"Errorf\"},\n\t\t\"(*testing.common).Fatal\": {\"(*testing.common)\", \"Fatalf\"},\n\t\t\"(testing.TB).Fatal\":      {\"(testing.TB)\", \"Fatalf\"},\n\t\t\"(*testing.common).Log\":   {\"(*testing.common)\", \"Logf\"},\n\t\t\"(testing.TB).Log\":        {\"(testing.TB)\", \"Logf\"},\n\t\t\"(*testing.common).Skip\":  {\"(*testing.common)\", \"Skipf\"},\n\t\t\"(testing.TB).Skip\":       {\"(testing.TB)\", \"Skipf\"},\n\t\t\"(*log.Logger).Fatal\":     {\"(*log.Logger)\", \"Fatalf\"},\n\t\t\"(*log.Logger).Fatalln\":   {\"(*log.Logger)\", \"Fatalf\"},\n\t\t\"(*log.Logger).Panic\":     {\"(*log.Logger)\", \"Panicf\"},\n\t\t\"(*log.Logger).Panicln\":   {\"(*log.Logger)\", \"Panicf\"},\n\t\t\"(*log.Logger).Print\":     {\"(*log.Logger)\", \"Printf\"},\n\t\t\"(*log.Logger).Println\":   {\"(*log.Logger)\", \"Printf\"},\n\t\t\"log.Fatal\":               {\"\", \"log.Fatalf\"},\n\t\t\"log.Fatalln\":             {\"\", \"log.Fatalf\"},\n\t\t\"log.Panic\":               {\"\", \"log.Panicf\"},\n\t\t\"log.Panicln\":             {\"\", \"log.Panicf\"},\n\t\t\"log.Print\":               {\"\", \"log.Printf\"},\n\t\t\"log.Println\":             {\"\", \"log.Printf\"},\n\t}\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfmtPrintf := func(node ast.Node) {\n\t\tm, ok := code.Match(pass, checkPrintSprintQ, node)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tname := m.State[\"fn\"].(*types.Func).Name()\n\t\tvar msg string\n\t\tswitch name {\n\t\tcase \"Print\", \"Fprint\", \"Sprint\":\n\t\t\tnewname := name + \"f\"\n\t\t\tmsg = fmt.Sprintf(\"should use fmt.%s instead of fmt.%s(fmt.Sprintf(...))\", newname, name)\n\t\tcase \"Println\", \"Fprintln\", \"Sprintln\":\n\t\t\tif _, ok := m.State[\"f\"].(*ast.BasicLit); !ok {\n\t\t\t\t// This may be an instance of\n\t\t\t\t// fmt.Println(fmt.Sprintf(arg, ...)) where arg is an\n\t\t\t\t// externally provided format string and the caller\n\t\t\t\t// cannot guarantee that the format string ends with a\n\t\t\t\t// newline.\n\t\t\t\treturn\n\t\t\t}\n\t\t\tnewname := name[:len(name)-2] + \"f\"\n\t\t\tmsg = fmt.Sprintf(\"should use fmt.%s instead of fmt.%s(fmt.Sprintf(...)) (but don't forget the newline)\", newname, name)\n\t\t}\n\t\treport.Report(pass, node, msg,\n\t\t\treport.FilterGenerated())\n\t}\n\n\tmethSprintf := func(node ast.Node) {\n\t\tm, ok := code.Match(pass, checkTestingErrorSprintfQ, node)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tmapped, ok := checkSprintfMapping[code.CallName(pass, node.(*ast.CallExpr))]\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\t// Ensure that Errorf/Fatalf refer to the right method\n\t\trecvTV, ok := pass.TypesInfo.Types[m.State[\"recv\"].(ast.Expr)]\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tobj, _, _ := types.LookupFieldOrMethod(recvTV.Type, recvTV.Addressable(), nil, mapped.alternative)\n\t\tf, ok := obj.(*types.Func)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tif typeutil.FuncName(f) != mapped.recv+\".\"+mapped.alternative {\n\t\t\treturn\n\t\t}\n\n\t\talt := &ast.SelectorExpr{\n\t\t\tX:   m.State[\"recv\"].(ast.Expr),\n\t\t\tSel: &ast.Ident{Name: mapped.alternative},\n\t\t}\n\t\treport.Report(pass, node, fmt.Sprintf(\"should use %s(...) instead of %s(fmt.Sprintf(...))\", report.Render(pass, alt), report.Render(pass, m.State[\"sel\"].(*ast.SelectorExpr))))\n\t}\n\n\tpkgSprintf := func(node ast.Node) {\n\t\t_, ok := code.Match(pass, checkLogSprintfQ, node)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tcallName := code.CallName(pass, node.(*ast.CallExpr))\n\t\tmapped, ok := checkSprintfMapping[callName]\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\treport.Report(pass, node, fmt.Sprintf(\"should use %s(...) instead of %s(fmt.Sprintf(...))\", mapped.alternative, callName))\n\t}\n\n\tfn := func(node ast.Node) {\n\t\tfmtPrintf(node)\n\t\t// TODO(dh): add suggested fixes\n\t\tmethSprintf(node)\n\t\tpkgSprintf(node)\n\t}\n\tif !code.CouldMatchAny(pass, checkLogSprintfQ, checkPrintSprintQ, checkTestingErrorSprintfQ) {\n\t\treturn nil, nil\n\t}\n\tcode.Preorder(pass, fn, (*ast.CallExpr)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1038/s1038_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1038\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1038/testdata/go1.0/CheckPrintSprintf/CheckPrintSprintf.go",
    "content": "package pkg\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"testing\"\n)\n\nfunc fn() {\n\tfmt.Print(fmt.Sprintf(\"%d\", 1))         //@ diag(`should use fmt.Printf`)\n\tfmt.Println(fmt.Sprintf(\"%d\", 1))       //@ diag(`don't forget the newline`)\n\tfmt.Fprint(nil, fmt.Sprintf(\"%d\", 1))   //@ diag(`should use fmt.Fprintf`)\n\tfmt.Fprintln(nil, fmt.Sprintf(\"%d\", 1)) //@ diag(`don't forget the newline`)\n\tfmt.Sprint(fmt.Sprintf(\"%d\", 1))        //@ diag(`should use fmt.Sprintf`)\n\tfmt.Sprintln(fmt.Sprintf(\"%d\", 1))      //@ diag(`don't forget the newline`)\n\n\targ := \"%d\"\n\tfmt.Println(fmt.Sprintf(arg, 1))\n}\n\nfunc Sprintf(string) string { return \"\" }\n\ntype Embedding1 struct {\n\t*testing.T\n}\n\ntype Embedding2 struct {\n\t*testing.T\n}\n\nfunc (e Embedding2) Errorf() {}\n\ntype Embedding3 struct {\n\t*testing.T\n}\n\nfunc (e Embedding3) Error(string, ...interface{}) {}\n\ntype Embedding4 struct {\n\ttesting.TB\n}\n\nfunc fn2() {\n\tvar t *testing.T\n\tvar b *testing.B\n\tvar tb testing.TB\n\n\t// All of these are the basic cases that should be flagged\n\tt.Error(fmt.Sprintf(\"\"))  //@ diag(`use t.Errorf(...) instead of t.Error(fmt.Sprintf(...))`)\n\tb.Error(fmt.Sprintf(\"\"))  //@ diag(`use b.Errorf`)\n\ttb.Error(fmt.Sprintf(\"\")) //@ diag(`use tb.Errorf`)\n\tt.Fatal(fmt.Sprintf(\"\"))  //@ diag(`use t.Fatalf`)\n\tb.Fatal(fmt.Sprintf(\"\"))  //@ diag(`use b.Fatalf`)\n\ttb.Fatal(fmt.Sprintf(\"\")) //@ diag(`use tb.Fatalf`)\n\tt.Log(fmt.Sprintf(\"\"))    //@ diag(`use t.Logf`)\n\tb.Log(fmt.Sprintf(\"\"))    //@ diag(`use b.Logf`)\n\ttb.Log(fmt.Sprintf(\"\"))   //@ diag(`use tb.Logf`)\n\tt.Skip(fmt.Sprintf(\"\"))   //@ diag(`use t.Skipf`)\n\tb.Skip(fmt.Sprintf(\"\"))   //@ diag(`use b.Skipf`)\n\ttb.Skip(fmt.Sprintf(\"\"))  //@ diag(`use tb.Skipf`)\n\n\tvar e1 Embedding1\n\tvar e2 Embedding2\n\tvar e3 Embedding3\n\tvar e4 Embedding4\n\t// Error and Errorf are both of *testing.common -> flag\n\te1.Error(fmt.Sprintf(\"\")) //@ diag(`use e1.Errorf`)\n\t// Fatal and Fatalf are both of *testing.common -> flag\n\te1.Fatal(fmt.Sprintf(\"\")) //@ diag(`use e1.Fatalf`)\n\t// Error is of *testing.common, but Errorf is Embedding2.Errorf -> don't flag\n\te2.Error(fmt.Sprintf(\"\"))\n\t// Fatal and Fatalf are both of *testing.common -> flag\n\te2.Fatal(fmt.Sprintf(\"\")) //@ diag(`use e2.Fatalf`)\n\t// Error is Embedding3.Error and Errorf is of *testing.common -> don't flag\n\te3.Error(fmt.Sprintf(\"\"))\n\t// Fatal and Fatalf are both of *testing.common -> flag\n\te3.Fatal(fmt.Sprintf(\"\")) //@ diag(`use e3.Fatalf`)\n\t// Error and Errorf are both of testing.TB -> flag\n\te4.Error(fmt.Sprintf(\"\")) //@ diag(`use e4.Errorf`)\n\t// Fatal and Fatalf are both of testing.TB -> flag\n\te4.Fatal(fmt.Sprintf(\"\")) //@ diag(`use e4.Fatalf`)\n\n\t// Basic cases\n\tlog.Fatal(fmt.Sprintf(\"\"))   //@ diag(`use log.Fatalf`)\n\tlog.Fatalln(fmt.Sprintf(\"\")) //@ diag(`use log.Fatalf`)\n\tlog.Panic(fmt.Sprintf(\"\"))   //@ diag(`use log.Panicf`)\n\tlog.Panicln(fmt.Sprintf(\"\")) //@ diag(`use log.Panicf`)\n\tlog.Print(fmt.Sprintf(\"\"))   //@ diag(`use log.Printf`)\n\tlog.Println(fmt.Sprintf(\"\")) //@ diag(`use log.Printf`)\n\n\tvar l *log.Logger\n\tl.Fatal(fmt.Sprintf(\"\"))   //@ diag(`use l.Fatalf(...) instead of l.Fatal(fmt.Sprintf(...)`)\n\tl.Fatalln(fmt.Sprintf(\"\")) //@ diag(`use l.Fatalf`)\n\tl.Panic(fmt.Sprintf(\"\"))   //@ diag(`use l.Panicf`)\n\tl.Panicln(fmt.Sprintf(\"\")) //@ diag(`use l.Panicf`)\n\tl.Print(fmt.Sprintf(\"\"))   //@ diag(`use l.Printf`)\n\tl.Println(fmt.Sprintf(\"\")) //@ diag(`use l.Printf`)\n\n\t// log.Logger and testing.T share a code path, no need to check embedding again\n}\n"
  },
  {
    "path": "simple/s1039/s1039.go",
    "content": "package s1039\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1039\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Unnecessary use of \\'fmt.Sprint\\'`,\n\t\tText: `\nCalling \\'fmt.Sprint\\' with a single string argument is unnecessary\nand identical to using the string directly.`,\n\t\tSince: \"2020.1\",\n\t\t// MergeIfAll because s might not be a string under all build tags.\n\t\t// you shouldn't write code like that…\n\t\tMergeIf: lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkSprintLiteralQ = pattern.MustParse(`\n\t(CallExpr\n\t\tfn@(Or\n\t\t\t(Symbol \"fmt.Sprint\")\n\t\t\t(Symbol \"fmt.Sprintf\"))\n\t\t[lit@(BasicLit \"STRING\" _)])`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// We only flag calls with string literals, not expressions of\n\t// type string, because some people use fmt.Sprint(s) as a pattern\n\t// for copying strings, which may be useful when extracting a small\n\t// substring from a large string.\n\n\tfor node, m := range code.Matches(pass, checkSprintLiteralQ) {\n\t\tcallee := m.State[\"fn\"].(*types.Func)\n\t\tlit := m.State[\"lit\"].(*ast.BasicLit)\n\t\tif callee.Name() == \"Sprintf\" {\n\t\t\tif strings.ContainsRune(lit.Value, '%') {\n\t\t\t\t// This might be a format string\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\treport.Report(pass, node, fmt.Sprintf(\"unnecessary use of fmt.%s\", callee.Name()),\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(\"Replace with string literal\", edit.ReplaceWithNode(pass.Fset, node, lit))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1039/s1039_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1039\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1039/testdata/go1.0/CheckSprintLiteral/CheckSprintLiteral.go",
    "content": "package pkg\n\nimport \"fmt\"\n\nfunc fn() {\n\t_ = fmt.Sprint(\"foo\")  //@ diag(`unnecessary use of fmt.Sprint`)\n\t_ = fmt.Sprintf(\"foo\") //@ diag(`unnecessary use of fmt.Sprintf`)\n\t_ = fmt.Sprintf(\"foo %d\")\n\t_ = fmt.Sprintf(\"foo %d\", 1)\n\n\tvar x string\n\t_ = fmt.Sprint(x)\n}\n"
  },
  {
    "path": "simple/s1039/testdata/go1.0/CheckSprintLiteral/CheckSprintLiteral.go.golden",
    "content": "package pkg\n\nimport \"fmt\"\n\nfunc fn() {\n\t_ = \"foo\" //@ diag(`unnecessary use of fmt.Sprint`)\n\t_ = \"foo\" //@ diag(`unnecessary use of fmt.Sprintf`)\n\t_ = fmt.Sprintf(\"foo %d\")\n\t_ = fmt.Sprintf(\"foo %d\", 1)\n\n\tvar x string\n\t_ = fmt.Sprint(x)\n}\n"
  },
  {
    "path": "simple/s1040/s1040.go",
    "content": "package s1040\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"S1040\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Type assertion to current type\",\n\t\tText: `The type assertion \\'x.(SomeInterface)\\', when \\'x\\' already has type\n\\'SomeInterface\\', can only fail if \\'x\\' is nil. Usually, this is\nleft-over code from when \\'x\\' had a different type and you can safely\ndelete the type assertion. If you want to check that \\'x\\' is not nil,\nconsider being explicit and using an actual \\'if x == nil\\' comparison\ninstead of relying on the type assertion panicking.`,\n\t\tSince: \"2021.1\",\n\t\t// MergeIfAll because x might have different types under different build tags.\n\t\t// You shouldn't write code like that…\n\t\tMergeIf: lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\texpr := node.(*ast.TypeAssertExpr)\n\t\tif expr.Type == nil {\n\t\t\t// skip type switches\n\t\t\t//\n\t\t\t// TODO(dh): we could flag type switches, too, when a case\n\t\t\t// statement has the same type as expr.X – however,\n\t\t\t// depending on the location of that case, it might behave\n\t\t\t// identically to a default branch. we need to think\n\t\t\t// carefully about the instances we want to flag. We also\n\t\t\t// have to take nil interface values into consideration.\n\t\t\t//\n\t\t\t// It might make more sense to extend SA4020 to handle\n\t\t\t// this.\n\t\t\treturn\n\t\t}\n\t\tt1 := pass.TypesInfo.TypeOf(expr.Type)\n\t\tt2 := pass.TypesInfo.TypeOf(expr.X)\n\t\tif types.IsInterface(t1) && types.Identical(t1, t2) {\n\t\t\treport.Report(pass, expr,\n\t\t\t\tfmt.Sprintf(\"type assertion to the same type: %s already has type %s\", report.Render(pass, expr.X), report.Render(pass, expr.Type)),\n\t\t\t\treport.FilterGenerated())\n\t\t}\n\t}\n\n\t// TODO(dh): add suggested fixes. we need different fixes depending on the context:\n\t// - assignment with 1 or 2 lhs\n\t// - assignment to blank identifiers (as the first, second or both lhs)\n\t// - initializers in if statements, with the same variations as above\n\n\tcode.Preorder(pass, fn, (*ast.TypeAssertExpr)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "simple/s1040/s1040_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage s1040\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "simple/s1040/testdata/go1.0/CheckSameTypeTypeAssertion/CheckSameTypeTypeAssertion.go",
    "content": "package pkg\n\ntype SomeInterface interface {\n\tFoo()\n}\n\nfunc fn(x SomeInterface) {\n\t_ = x.(SomeInterface)                   //@ diag(`type assertion to the same type: x already has type SomeInterface`)\n\ty := x.(SomeInterface)                  //@ diag(`type assertion to the same type`)\n\ty = x.(SomeInterface)                   //@ diag(`type assertion to the same type`)\n\tvar a SomeInterface = x.(SomeInterface) //@ diag(`type assertion to the same type`)\n\tz, _ := x.(SomeInterface)               //@ diag(`type assertion to the same type`)\n\tz, _ = x.(SomeInterface)                //@ diag(`type assertion to the same type`)\n\n\t_, ok := x.(SomeInterface) //@ diag(`type assertion to the same type`)\n\t_, ok = x.(SomeInterface)  //@ diag(`type assertion to the same type`)\n\t_, _ = x.(SomeInterface)   //@ diag(`type assertion to the same type`)\n\n\tif z, ok := x.(SomeInterface); ok { //@ diag(`type assertion to the same type`)\n\t\t_ = z\n\t}\n\tif _, ok := x.(SomeInterface); !ok { //@ diag(`type assertion to the same type`)\n\t}\n\tif _, ok = x.(SomeInterface); !ok { //@ diag(`type assertion to the same type`)\n\t}\n\tif z, ok = x.(SomeInterface); ok { //@ diag(`type assertion to the same type`)\n\t}\n\tif z := x.(SomeInterface); true { //@ diag(`type assertion to the same type`)\n\t\t_ = z\n\t}\n\tif z, _ := x.(SomeInterface); true { //@ diag(`type assertion to the same type`)\n\t\t_ = z\n\t}\n\tif _, _ = x.(SomeInterface); true { //@ diag(`type assertion to the same type`)\n\t}\n\tif _ = x.(SomeInterface); true { //@ diag(`type assertion to the same type`)\n\t}\n\n\tswitch x.(type) {\n\tcase SomeInterface:\n\t}\n\n\t_ = a\n\t_ = y\n\t_ = ok\n\t_ = z\n}\n"
  },
  {
    "path": "staticcheck/analysis.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage staticcheck\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/staticcheck/sa1000\"\n\t\"honnef.co/go/tools/staticcheck/sa1001\"\n\t\"honnef.co/go/tools/staticcheck/sa1002\"\n\t\"honnef.co/go/tools/staticcheck/sa1003\"\n\t\"honnef.co/go/tools/staticcheck/sa1004\"\n\t\"honnef.co/go/tools/staticcheck/sa1005\"\n\t\"honnef.co/go/tools/staticcheck/sa1006\"\n\t\"honnef.co/go/tools/staticcheck/sa1007\"\n\t\"honnef.co/go/tools/staticcheck/sa1008\"\n\t\"honnef.co/go/tools/staticcheck/sa1010\"\n\t\"honnef.co/go/tools/staticcheck/sa1011\"\n\t\"honnef.co/go/tools/staticcheck/sa1012\"\n\t\"honnef.co/go/tools/staticcheck/sa1013\"\n\t\"honnef.co/go/tools/staticcheck/sa1014\"\n\t\"honnef.co/go/tools/staticcheck/sa1015\"\n\t\"honnef.co/go/tools/staticcheck/sa1016\"\n\t\"honnef.co/go/tools/staticcheck/sa1017\"\n\t\"honnef.co/go/tools/staticcheck/sa1018\"\n\t\"honnef.co/go/tools/staticcheck/sa1019\"\n\t\"honnef.co/go/tools/staticcheck/sa1020\"\n\t\"honnef.co/go/tools/staticcheck/sa1021\"\n\t\"honnef.co/go/tools/staticcheck/sa1023\"\n\t\"honnef.co/go/tools/staticcheck/sa1024\"\n\t\"honnef.co/go/tools/staticcheck/sa1025\"\n\t\"honnef.co/go/tools/staticcheck/sa1026\"\n\t\"honnef.co/go/tools/staticcheck/sa1027\"\n\t\"honnef.co/go/tools/staticcheck/sa1028\"\n\t\"honnef.co/go/tools/staticcheck/sa1029\"\n\t\"honnef.co/go/tools/staticcheck/sa1030\"\n\t\"honnef.co/go/tools/staticcheck/sa1031\"\n\t\"honnef.co/go/tools/staticcheck/sa1032\"\n\t\"honnef.co/go/tools/staticcheck/sa2000\"\n\t\"honnef.co/go/tools/staticcheck/sa2001\"\n\t\"honnef.co/go/tools/staticcheck/sa2002\"\n\t\"honnef.co/go/tools/staticcheck/sa2003\"\n\t\"honnef.co/go/tools/staticcheck/sa3000\"\n\t\"honnef.co/go/tools/staticcheck/sa3001\"\n\t\"honnef.co/go/tools/staticcheck/sa4000\"\n\t\"honnef.co/go/tools/staticcheck/sa4001\"\n\t\"honnef.co/go/tools/staticcheck/sa4003\"\n\t\"honnef.co/go/tools/staticcheck/sa4004\"\n\t\"honnef.co/go/tools/staticcheck/sa4005\"\n\t\"honnef.co/go/tools/staticcheck/sa4006\"\n\t\"honnef.co/go/tools/staticcheck/sa4008\"\n\t\"honnef.co/go/tools/staticcheck/sa4009\"\n\t\"honnef.co/go/tools/staticcheck/sa4010\"\n\t\"honnef.co/go/tools/staticcheck/sa4011\"\n\t\"honnef.co/go/tools/staticcheck/sa4012\"\n\t\"honnef.co/go/tools/staticcheck/sa4013\"\n\t\"honnef.co/go/tools/staticcheck/sa4014\"\n\t\"honnef.co/go/tools/staticcheck/sa4015\"\n\t\"honnef.co/go/tools/staticcheck/sa4016\"\n\t\"honnef.co/go/tools/staticcheck/sa4017\"\n\t\"honnef.co/go/tools/staticcheck/sa4018\"\n\t\"honnef.co/go/tools/staticcheck/sa4019\"\n\t\"honnef.co/go/tools/staticcheck/sa4020\"\n\t\"honnef.co/go/tools/staticcheck/sa4021\"\n\t\"honnef.co/go/tools/staticcheck/sa4022\"\n\t\"honnef.co/go/tools/staticcheck/sa4023\"\n\t\"honnef.co/go/tools/staticcheck/sa4024\"\n\t\"honnef.co/go/tools/staticcheck/sa4025\"\n\t\"honnef.co/go/tools/staticcheck/sa4026\"\n\t\"honnef.co/go/tools/staticcheck/sa4027\"\n\t\"honnef.co/go/tools/staticcheck/sa4028\"\n\t\"honnef.co/go/tools/staticcheck/sa4029\"\n\t\"honnef.co/go/tools/staticcheck/sa4030\"\n\t\"honnef.co/go/tools/staticcheck/sa4031\"\n\t\"honnef.co/go/tools/staticcheck/sa4032\"\n\t\"honnef.co/go/tools/staticcheck/sa5000\"\n\t\"honnef.co/go/tools/staticcheck/sa5001\"\n\t\"honnef.co/go/tools/staticcheck/sa5002\"\n\t\"honnef.co/go/tools/staticcheck/sa5003\"\n\t\"honnef.co/go/tools/staticcheck/sa5004\"\n\t\"honnef.co/go/tools/staticcheck/sa5005\"\n\t\"honnef.co/go/tools/staticcheck/sa5007\"\n\t\"honnef.co/go/tools/staticcheck/sa5008\"\n\t\"honnef.co/go/tools/staticcheck/sa5009\"\n\t\"honnef.co/go/tools/staticcheck/sa5010\"\n\t\"honnef.co/go/tools/staticcheck/sa5011\"\n\t\"honnef.co/go/tools/staticcheck/sa5012\"\n\t\"honnef.co/go/tools/staticcheck/sa6000\"\n\t\"honnef.co/go/tools/staticcheck/sa6001\"\n\t\"honnef.co/go/tools/staticcheck/sa6002\"\n\t\"honnef.co/go/tools/staticcheck/sa6003\"\n\t\"honnef.co/go/tools/staticcheck/sa6005\"\n\t\"honnef.co/go/tools/staticcheck/sa6006\"\n\t\"honnef.co/go/tools/staticcheck/sa9001\"\n\t\"honnef.co/go/tools/staticcheck/sa9002\"\n\t\"honnef.co/go/tools/staticcheck/sa9003\"\n\t\"honnef.co/go/tools/staticcheck/sa9004\"\n\t\"honnef.co/go/tools/staticcheck/sa9005\"\n\t\"honnef.co/go/tools/staticcheck/sa9006\"\n\t\"honnef.co/go/tools/staticcheck/sa9007\"\n\t\"honnef.co/go/tools/staticcheck/sa9008\"\n\t\"honnef.co/go/tools/staticcheck/sa9009\"\n\t\"honnef.co/go/tools/staticcheck/sa9010\"\n)\n\nvar Analyzers = []*lint.Analyzer{\n\tsa1000.SCAnalyzer,\n\tsa1001.SCAnalyzer,\n\tsa1002.SCAnalyzer,\n\tsa1003.SCAnalyzer,\n\tsa1004.SCAnalyzer,\n\tsa1005.SCAnalyzer,\n\tsa1006.SCAnalyzer,\n\tsa1007.SCAnalyzer,\n\tsa1008.SCAnalyzer,\n\tsa1010.SCAnalyzer,\n\tsa1011.SCAnalyzer,\n\tsa1012.SCAnalyzer,\n\tsa1013.SCAnalyzer,\n\tsa1014.SCAnalyzer,\n\tsa1015.SCAnalyzer,\n\tsa1016.SCAnalyzer,\n\tsa1017.SCAnalyzer,\n\tsa1018.SCAnalyzer,\n\tsa1019.SCAnalyzer,\n\tsa1020.SCAnalyzer,\n\tsa1021.SCAnalyzer,\n\tsa1023.SCAnalyzer,\n\tsa1024.SCAnalyzer,\n\tsa1025.SCAnalyzer,\n\tsa1026.SCAnalyzer,\n\tsa1027.SCAnalyzer,\n\tsa1028.SCAnalyzer,\n\tsa1029.SCAnalyzer,\n\tsa1030.SCAnalyzer,\n\tsa1031.SCAnalyzer,\n\tsa1032.SCAnalyzer,\n\tsa2000.SCAnalyzer,\n\tsa2001.SCAnalyzer,\n\tsa2002.SCAnalyzer,\n\tsa2003.SCAnalyzer,\n\tsa3000.SCAnalyzer,\n\tsa3001.SCAnalyzer,\n\tsa4000.SCAnalyzer,\n\tsa4001.SCAnalyzer,\n\tsa4003.SCAnalyzer,\n\tsa4004.SCAnalyzer,\n\tsa4005.SCAnalyzer,\n\tsa4006.SCAnalyzer,\n\tsa4008.SCAnalyzer,\n\tsa4009.SCAnalyzer,\n\tsa4010.SCAnalyzer,\n\tsa4011.SCAnalyzer,\n\tsa4012.SCAnalyzer,\n\tsa4013.SCAnalyzer,\n\tsa4014.SCAnalyzer,\n\tsa4015.SCAnalyzer,\n\tsa4016.SCAnalyzer,\n\tsa4017.SCAnalyzer,\n\tsa4018.SCAnalyzer,\n\tsa4019.SCAnalyzer,\n\tsa4020.SCAnalyzer,\n\tsa4021.SCAnalyzer,\n\tsa4022.SCAnalyzer,\n\tsa4023.SCAnalyzer,\n\tsa4024.SCAnalyzer,\n\tsa4025.SCAnalyzer,\n\tsa4026.SCAnalyzer,\n\tsa4027.SCAnalyzer,\n\tsa4028.SCAnalyzer,\n\tsa4029.SCAnalyzer,\n\tsa4030.SCAnalyzer,\n\tsa4031.SCAnalyzer,\n\tsa4032.SCAnalyzer,\n\tsa5000.SCAnalyzer,\n\tsa5001.SCAnalyzer,\n\tsa5002.SCAnalyzer,\n\tsa5003.SCAnalyzer,\n\tsa5004.SCAnalyzer,\n\tsa5005.SCAnalyzer,\n\tsa5007.SCAnalyzer,\n\tsa5008.SCAnalyzer,\n\tsa5009.SCAnalyzer,\n\tsa5010.SCAnalyzer,\n\tsa5011.SCAnalyzer,\n\tsa5012.SCAnalyzer,\n\tsa6000.SCAnalyzer,\n\tsa6001.SCAnalyzer,\n\tsa6002.SCAnalyzer,\n\tsa6003.SCAnalyzer,\n\tsa6005.SCAnalyzer,\n\tsa6006.SCAnalyzer,\n\tsa9001.SCAnalyzer,\n\tsa9002.SCAnalyzer,\n\tsa9003.SCAnalyzer,\n\tsa9004.SCAnalyzer,\n\tsa9005.SCAnalyzer,\n\tsa9006.SCAnalyzer,\n\tsa9007.SCAnalyzer,\n\tsa9008.SCAnalyzer,\n\tsa9009.SCAnalyzer,\n\tsa9010.SCAnalyzer,\n}\n"
  },
  {
    "path": "staticcheck/doc.go",
    "content": "//go:generate go run ../generate.go\n\n// Package staticcheck contains analyzes that find bugs and performance issues.\n// Barring the rare false positive, any code flagged by these analyzes needs to be fixed.\npackage staticcheck\n"
  },
  {
    "path": "staticcheck/fakejson/encode.go",
    "content": "// Copyright 2010 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// This file contains a modified copy of the encoding/json encoder.\n// All dynamic behavior has been removed, and reflecttion has been replaced with go/types.\n// This allows us to statically find unmarshable types\n// with the same rules for tags, shadowing and addressability as encoding/json.\n// This is used for SA1026.\n\npackage fakejson\n\nimport (\n\t\"go/types\"\n\t\"sort\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"golang.org/x/exp/typeparams\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/knowledge\"\n\t\"honnef.co/go/tools/staticcheck/fakereflect\"\n)\n\n// parseTag splits a struct field's json tag into its name and\n// comma-separated options.\nfunc parseTag(tag string) string {\n\tif before, _, ok := strings.Cut(tag, \",\"); ok {\n\t\treturn before\n\t}\n\treturn tag\n}\n\nfunc Marshal(v types.Type) *UnsupportedTypeError {\n\tenc := encoder{}\n\treturn enc.newTypeEncoder(fakereflect.TypeAndCanAddr{Type: v}, \"x\")\n}\n\n// An UnsupportedTypeError is returned by Marshal when attempting\n// to encode an unsupported value type.\ntype UnsupportedTypeError struct {\n\tType types.Type\n\tPath string\n}\n\ntype encoder struct {\n\t// TODO we track addressable and non-addressable instances separately out of an abundance of caution. We don't know\n\t// if this is actually required for correctness.\n\tseenCanAddr  typeutil.Map[struct{}]\n\tseenCantAddr typeutil.Map[struct{}]\n}\n\nfunc (enc *encoder) newTypeEncoder(t fakereflect.TypeAndCanAddr, stack string) *UnsupportedTypeError {\n\tvar m *typeutil.Map[struct{}]\n\tif t.CanAddr() {\n\t\tm = &enc.seenCanAddr\n\t} else {\n\t\tm = &enc.seenCantAddr\n\t}\n\tif _, ok := m.At(t.Type); ok {\n\t\treturn nil\n\t}\n\tm.Set(t.Type, struct{}{})\n\n\tif t.Implements(knowledge.Interfaces[\"encoding/json.Marshaler\"]) {\n\t\treturn nil\n\t}\n\tif !t.IsPtr() && t.CanAddr() && fakereflect.PtrTo(t).Implements(knowledge.Interfaces[\"encoding/json.Marshaler\"]) {\n\t\treturn nil\n\t}\n\tif t.Implements(knowledge.Interfaces[\"encoding.TextMarshaler\"]) {\n\t\treturn nil\n\t}\n\tif !t.IsPtr() && t.CanAddr() && fakereflect.PtrTo(t).Implements(knowledge.Interfaces[\"encoding.TextMarshaler\"]) {\n\t\treturn nil\n\t}\n\n\tswitch t.Type.Underlying().(type) {\n\tcase *types.Basic, *types.Interface:\n\t\treturn nil\n\tcase *types.Struct:\n\t\treturn enc.typeFields(t, stack)\n\tcase *types.Map:\n\t\treturn enc.newMapEncoder(t, stack)\n\tcase *types.Slice:\n\t\treturn enc.newSliceEncoder(t, stack)\n\tcase *types.Array:\n\t\treturn enc.newArrayEncoder(t, stack)\n\tcase *types.Pointer:\n\t\t// we don't have to express the pointer dereference in the path; x.f is syntactic sugar for (*x).f\n\t\treturn enc.newTypeEncoder(t.Elem(), stack)\n\tdefault:\n\t\treturn &UnsupportedTypeError{t.Type, stack}\n\t}\n}\n\nfunc (enc *encoder) newMapEncoder(t fakereflect.TypeAndCanAddr, stack string) *UnsupportedTypeError {\n\tif typeparams.IsTypeParam(t.Key().Type) {\n\t\t// We don't know enough about the concrete instantiation to say much about the key. The only time we could make\n\t\t// a definite \"this key is bad\" statement is if the type parameter is constrained by type terms, none of which\n\t\t// are tilde terms, none of which are a basic type. In all other cases, the key might implement TextMarshaler.\n\t\t// It doesn't seem worth checking for that one single case.\n\t\treturn enc.newTypeEncoder(t.Elem(), stack+\"[k]\")\n\t}\n\n\tswitch t.Key().Type.Underlying().(type) {\n\tcase *types.Basic:\n\tdefault:\n\t\tif !t.Key().Implements(knowledge.Interfaces[\"encoding.TextMarshaler\"]) {\n\t\t\treturn &UnsupportedTypeError{\n\t\t\t\tType: t.Type,\n\t\t\t\tPath: stack,\n\t\t\t}\n\t\t}\n\t}\n\treturn enc.newTypeEncoder(t.Elem(), stack+\"[k]\")\n}\n\nfunc (enc *encoder) newSliceEncoder(t fakereflect.TypeAndCanAddr, stack string) *UnsupportedTypeError {\n\t// Byte slices get special treatment; arrays don't.\n\tbasic, ok := t.Elem().Type.Underlying().(*types.Basic)\n\tif ok && basic.Kind() == types.Uint8 {\n\t\tp := fakereflect.PtrTo(t.Elem())\n\t\tif !p.Implements(knowledge.Interfaces[\"encoding/json.Marshaler\"]) && !p.Implements(knowledge.Interfaces[\"encoding.TextMarshaler\"]) {\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn enc.newArrayEncoder(t, stack)\n}\n\nfunc (enc *encoder) newArrayEncoder(t fakereflect.TypeAndCanAddr, stack string) *UnsupportedTypeError {\n\treturn enc.newTypeEncoder(t.Elem(), stack+\"[0]\")\n}\n\nfunc isValidTag(s string) bool {\n\tif s == \"\" {\n\t\treturn false\n\t}\n\tfor _, c := range s {\n\t\tswitch {\n\t\tcase strings.ContainsRune(\"!#$%&()*+-./:;<=>?@[]^_{|}~ \", c):\n\t\t\t// Backslash and quote chars are reserved, but\n\t\t\t// otherwise any punctuation chars are allowed\n\t\t\t// in a tag name.\n\t\tcase !unicode.IsLetter(c) && !unicode.IsDigit(c):\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc typeByIndex(t fakereflect.TypeAndCanAddr, index []int) fakereflect.TypeAndCanAddr {\n\tfor _, i := range index {\n\t\tif t.IsPtr() {\n\t\t\tt = t.Elem()\n\t\t}\n\t\tt = t.Field(i).Type\n\t}\n\treturn t\n}\n\nfunc pathByIndex(t fakereflect.TypeAndCanAddr, index []int) string {\n\tvar path strings.Builder\n\tfor _, i := range index {\n\t\tif t.IsPtr() {\n\t\t\tt = t.Elem()\n\t\t}\n\t\tpath.WriteString(\".\" + t.Field(i).Name)\n\t\tt = t.Field(i).Type\n\t}\n\treturn path.String()\n}\n\n// A field represents a single field found in a struct.\ntype field struct {\n\tname string\n\n\ttag   bool\n\tindex []int\n\ttyp   fakereflect.TypeAndCanAddr\n}\n\n// byIndex sorts field by index sequence.\ntype byIndex []field\n\nfunc (x byIndex) Len() int { return len(x) }\n\nfunc (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }\n\nfunc (x byIndex) Less(i, j int) bool {\n\tfor k, xik := range x[i].index {\n\t\tif k >= len(x[j].index) {\n\t\t\treturn false\n\t\t}\n\t\tif xik != x[j].index[k] {\n\t\t\treturn xik < x[j].index[k]\n\t\t}\n\t}\n\treturn len(x[i].index) < len(x[j].index)\n}\n\n// typeFields returns a list of fields that JSON should recognize for the given type.\n// The algorithm is breadth-first search over the set of structs to include - the top struct\n// and then any reachable anonymous structs.\nfunc (enc *encoder) typeFields(t fakereflect.TypeAndCanAddr, stack string) *UnsupportedTypeError {\n\t// Anonymous fields to explore at the current level and the next.\n\tcurrent := []field{}\n\tnext := []field{{typ: t}}\n\n\t// Count of queued names for current level and the next.\n\tvar count, nextCount map[fakereflect.TypeAndCanAddr]int\n\n\t// Types already visited at an earlier level.\n\tvisited := map[fakereflect.TypeAndCanAddr]bool{}\n\n\t// Fields found.\n\tvar fields []field\n\n\tfor len(next) > 0 {\n\t\tcurrent, next = next, current[:0]\n\t\tcount, nextCount = nextCount, map[fakereflect.TypeAndCanAddr]int{}\n\n\t\tfor _, f := range current {\n\t\t\tif visited[f.typ] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvisited[f.typ] = true\n\n\t\t\t// Scan f.typ for fields to include.\n\t\t\tfor i := 0; i < f.typ.NumField(); i++ {\n\t\t\t\tsf := f.typ.Field(i)\n\t\t\t\tif sf.Anonymous {\n\t\t\t\t\tt := sf.Type\n\t\t\t\t\tif t.IsPtr() {\n\t\t\t\t\t\tt = t.Elem()\n\t\t\t\t\t}\n\t\t\t\t\tif !sf.IsExported() && !t.IsStruct() {\n\t\t\t\t\t\t// Ignore embedded fields of unexported non-struct types.\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\t// Do not ignore embedded fields of unexported struct types\n\t\t\t\t\t// since they may have exported fields.\n\t\t\t\t} else if !sf.IsExported() {\n\t\t\t\t\t// Ignore unexported non-embedded fields.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\ttag := sf.Tag.Get(\"json\")\n\t\t\t\tif tag == \"-\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tname := parseTag(tag)\n\t\t\t\tif !isValidTag(name) {\n\t\t\t\t\tname = \"\"\n\t\t\t\t}\n\t\t\t\tindex := make([]int, len(f.index)+1)\n\t\t\t\tcopy(index, f.index)\n\t\t\t\tindex[len(f.index)] = i\n\n\t\t\t\tft := sf.Type\n\t\t\t\tif ft.Name() == \"\" && ft.IsPtr() {\n\t\t\t\t\t// Follow pointer.\n\t\t\t\t\tft = ft.Elem()\n\t\t\t\t}\n\n\t\t\t\t// Record found field and index sequence.\n\t\t\t\tif name != \"\" || !sf.Anonymous || !ft.IsStruct() {\n\t\t\t\t\ttagged := name != \"\"\n\t\t\t\t\tif name == \"\" {\n\t\t\t\t\t\tname = sf.Name\n\t\t\t\t\t}\n\t\t\t\t\tfield := field{\n\t\t\t\t\t\tname:  name,\n\t\t\t\t\t\ttag:   tagged,\n\t\t\t\t\t\tindex: index,\n\t\t\t\t\t\ttyp:   ft,\n\t\t\t\t\t}\n\n\t\t\t\t\tfields = append(fields, field)\n\t\t\t\t\tif count[f.typ] > 1 {\n\t\t\t\t\t\t// If there were multiple instances, add a second,\n\t\t\t\t\t\t// so that the annihilation code will see a duplicate.\n\t\t\t\t\t\t// It only cares about the distinction between 1 or 2,\n\t\t\t\t\t\t// so don't bother generating any more copies.\n\t\t\t\t\t\tfields = append(fields, fields[len(fields)-1])\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Record new anonymous struct to explore in next round.\n\t\t\t\tnextCount[ft]++\n\t\t\t\tif nextCount[ft] == 1 {\n\t\t\t\t\tnext = append(next, field{name: ft.Name(), index: index, typ: ft})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tsort.Slice(fields, func(i, j int) bool {\n\t\tx := fields\n\t\t// sort field by name, breaking ties with depth, then\n\t\t// breaking ties with \"name came from json tag\", then\n\t\t// breaking ties with index sequence.\n\t\tif x[i].name != x[j].name {\n\t\t\treturn x[i].name < x[j].name\n\t\t}\n\t\tif len(x[i].index) != len(x[j].index) {\n\t\t\treturn len(x[i].index) < len(x[j].index)\n\t\t}\n\t\tif x[i].tag != x[j].tag {\n\t\t\treturn x[i].tag\n\t\t}\n\t\treturn byIndex(x).Less(i, j)\n\t})\n\n\t// Delete all fields that are hidden by the Go rules for embedded fields,\n\t// except that fields with JSON tags are promoted.\n\n\t// The fields are sorted in primary order of name, secondary order\n\t// of field index length. Loop over names; for each name, delete\n\t// hidden fields by choosing the one dominant field that survives.\n\tout := fields[:0]\n\tfor advance, i := 0, 0; i < len(fields); i += advance {\n\t\t// One iteration per name.\n\t\t// Find the sequence of fields with the name of this first field.\n\t\tfi := fields[i]\n\t\tname := fi.name\n\t\tfor advance = 1; i+advance < len(fields); advance++ {\n\t\t\tfj := fields[i+advance]\n\t\t\tif fj.name != name {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif advance == 1 { // Only one field with this name\n\t\t\tout = append(out, fi)\n\t\t\tcontinue\n\t\t}\n\t\tdominant, ok := dominantField(fields[i : i+advance])\n\t\tif ok {\n\t\t\tout = append(out, dominant)\n\t\t}\n\t}\n\n\tfields = out\n\tsort.Sort(byIndex(fields))\n\n\tfor i := range fields {\n\t\tf := &fields[i]\n\t\terr := enc.newTypeEncoder(typeByIndex(t, f.index), stack+pathByIndex(t, f.index))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// dominantField looks through the fields, all of which are known to\n// have the same name, to find the single field that dominates the\n// others using Go's embedding rules, modified by the presence of\n// JSON tags. If there are multiple top-level fields, the boolean\n// will be false: This condition is an error in Go and we skip all\n// the fields.\nfunc dominantField(fields []field) (field, bool) {\n\t// The fields are sorted in increasing index-length order, then by presence of tag.\n\t// That means that the first field is the dominant one. We need only check\n\t// for error cases: two fields at top level, either both tagged or neither tagged.\n\tif len(fields) > 1 && len(fields[0].index) == len(fields[1].index) && fields[0].tag == fields[1].tag {\n\t\treturn field{}, false\n\t}\n\treturn fields[0], true\n}\n"
  },
  {
    "path": "staticcheck/fakereflect/fakereflect.go",
    "content": "package fakereflect\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\t\"reflect\"\n)\n\ntype TypeAndCanAddr struct {\n\tType    types.Type\n\tcanAddr bool\n}\n\ntype StructField struct {\n\tIndex     []int\n\tName      string\n\tAnonymous bool\n\tTag       reflect.StructTag\n\tf         *types.Var\n\tType      TypeAndCanAddr\n}\n\nfunc (sf StructField) IsExported() bool { return sf.f.Exported() }\n\nfunc (t TypeAndCanAddr) Field(i int) StructField {\n\tst := t.Type.Underlying().(*types.Struct)\n\tf := st.Field(i)\n\treturn StructField{\n\t\tf:         f,\n\t\tIndex:     []int{i},\n\t\tName:      f.Name(),\n\t\tAnonymous: f.Anonymous(),\n\t\tTag:       reflect.StructTag(st.Tag(i)),\n\t\tType: TypeAndCanAddr{\n\t\t\tType:    f.Type(),\n\t\t\tcanAddr: t.canAddr,\n\t\t},\n\t}\n}\n\nfunc (t TypeAndCanAddr) FieldByIndex(index []int) StructField {\n\tf := t.Field(index[0])\n\tfor _, idx := range index[1:] {\n\t\tf = f.Type.Field(idx)\n\t}\n\tf.Index = index\n\treturn f\n}\n\nfunc PtrTo(t TypeAndCanAddr) TypeAndCanAddr {\n\t// Note that we don't care about canAddr here because it's irrelevant to all uses of PtrTo\n\treturn TypeAndCanAddr{Type: types.NewPointer(t.Type)}\n}\n\nfunc (t TypeAndCanAddr) CanAddr() bool { return t.canAddr }\n\nfunc (t TypeAndCanAddr) Implements(ityp *types.Interface) bool {\n\treturn types.Implements(t.Type, ityp)\n}\n\nfunc (t TypeAndCanAddr) IsSlice() bool {\n\t_, ok := t.Type.Underlying().(*types.Slice)\n\treturn ok\n}\n\nfunc (t TypeAndCanAddr) IsArray() bool {\n\t_, ok := t.Type.Underlying().(*types.Array)\n\treturn ok\n}\n\nfunc (t TypeAndCanAddr) IsPtr() bool {\n\t_, ok := t.Type.Underlying().(*types.Pointer)\n\treturn ok\n}\n\nfunc (t TypeAndCanAddr) IsInterface() bool {\n\t_, ok := t.Type.Underlying().(*types.Interface)\n\treturn ok\n}\n\nfunc (t TypeAndCanAddr) IsStruct() bool {\n\t_, ok := t.Type.Underlying().(*types.Struct)\n\treturn ok\n}\n\nfunc (t TypeAndCanAddr) Name() string {\n\tnamed, ok := types.Unalias(t.Type).(*types.Named)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\treturn named.Obj().Name()\n}\n\nfunc (t TypeAndCanAddr) NumField() int {\n\treturn t.Type.Underlying().(*types.Struct).NumFields()\n}\n\nfunc (t TypeAndCanAddr) String() string {\n\treturn t.Type.String()\n}\n\nfunc (t TypeAndCanAddr) Key() TypeAndCanAddr {\n\treturn TypeAndCanAddr{Type: t.Type.Underlying().(*types.Map).Key()}\n}\n\nfunc (t TypeAndCanAddr) Elem() TypeAndCanAddr {\n\tswitch typ := t.Type.Underlying().(type) {\n\tcase *types.Pointer:\n\t\treturn TypeAndCanAddr{\n\t\t\tType:    typ.Elem(),\n\t\t\tcanAddr: true,\n\t\t}\n\tcase *types.Slice:\n\t\treturn TypeAndCanAddr{\n\t\t\tType:    typ.Elem(),\n\t\t\tcanAddr: true,\n\t\t}\n\tcase *types.Array:\n\t\treturn TypeAndCanAddr{\n\t\t\tType:    typ.Elem(),\n\t\t\tcanAddr: t.canAddr,\n\t\t}\n\tcase *types.Map:\n\t\treturn TypeAndCanAddr{\n\t\t\tType:    typ.Elem(),\n\t\t\tcanAddr: false,\n\t\t}\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unhandled type %T\", typ))\n\t}\n}\n"
  },
  {
    "path": "staticcheck/fakexml/marshal.go",
    "content": "// Copyright 2011 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// This file contains a modified copy of the encoding/xml encoder.\n// All dynamic behavior has been removed, and reflecttion has been replaced with go/types.\n// This allows us to statically find unmarshable types\n// with the same rules for tags, shadowing and addressability as encoding/xml.\n// This is used for SA1026 and SA5008.\n\n// NOTE(dh): we do not check CanInterface in various places, which means we'll accept more marshaler implementations than encoding/xml does. This will lead to a small amount of false negatives.\n\npackage fakexml\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/knowledge\"\n\t\"honnef.co/go/tools/staticcheck/fakereflect\"\n)\n\nfunc Marshal(v types.Type) error {\n\treturn NewEncoder().Encode(v)\n}\n\ntype Encoder struct {\n\t// TODO we track addressable and non-addressable instances separately out of an abundance of caution. We don't know\n\t// if this is actually required for correctness.\n\tseenCanAddr  typeutil.Map[struct{}]\n\tseenCantAddr typeutil.Map[struct{}]\n}\n\nfunc NewEncoder() *Encoder {\n\te := &Encoder{}\n\treturn e\n}\n\nfunc (enc *Encoder) Encode(v types.Type) error {\n\trv := fakereflect.TypeAndCanAddr{Type: v}\n\treturn enc.marshalValue(rv, nil, nil, \"x\")\n}\n\nfunc implementsMarshaler(v fakereflect.TypeAndCanAddr) bool {\n\tt := v.Type\n\tobj, _, _ := types.LookupFieldOrMethod(t, false, nil, \"MarshalXML\")\n\tif obj == nil {\n\t\treturn false\n\t}\n\tfn, ok := obj.(*types.Func)\n\tif !ok {\n\t\treturn false\n\t}\n\tparams := fn.Type().(*types.Signature).Params()\n\tif params.Len() != 2 {\n\t\treturn false\n\t}\n\tif !typeutil.IsPointerToTypeWithName(params.At(0).Type(), \"encoding/xml.Encoder\") {\n\t\treturn false\n\t}\n\tif !typeutil.IsTypeWithName(params.At(1).Type(), \"encoding/xml.StartElement\") {\n\t\treturn false\n\t}\n\trets := fn.Type().(*types.Signature).Results()\n\tif rets.Len() != 1 {\n\t\treturn false\n\t}\n\tif !typeutil.IsTypeWithName(rets.At(0).Type(), \"error\") {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc implementsMarshalerAttr(v fakereflect.TypeAndCanAddr) bool {\n\tt := v.Type\n\tobj, _, _ := types.LookupFieldOrMethod(t, false, nil, \"MarshalXMLAttr\")\n\tif obj == nil {\n\t\treturn false\n\t}\n\tfn, ok := obj.(*types.Func)\n\tif !ok {\n\t\treturn false\n\t}\n\tparams := fn.Type().(*types.Signature).Params()\n\tif params.Len() != 1 {\n\t\treturn false\n\t}\n\tif !typeutil.IsTypeWithName(params.At(0).Type(), \"encoding/xml.Name\") {\n\t\treturn false\n\t}\n\trets := fn.Type().(*types.Signature).Results()\n\tif rets.Len() != 2 {\n\t\treturn false\n\t}\n\tif !typeutil.IsTypeWithName(rets.At(0).Type(), \"encoding/xml.Attr\") {\n\t\treturn false\n\t}\n\tif !typeutil.IsTypeWithName(rets.At(1).Type(), \"error\") {\n\t\treturn false\n\t}\n\treturn true\n}\n\ntype CyclicTypeError struct {\n\tType types.Type\n\tPath string\n}\n\nfunc (err *CyclicTypeError) Error() string {\n\treturn \"cyclic type\"\n}\n\n// marshalValue writes one or more XML elements representing val.\n// If val was obtained from a struct field, finfo must have its details.\nfunc (e *Encoder) marshalValue(val fakereflect.TypeAndCanAddr, finfo *fieldInfo, startTemplate *StartElement, stack string) error {\n\tvar m *typeutil.Map[struct{}]\n\tif val.CanAddr() {\n\t\tm = &e.seenCanAddr\n\t} else {\n\t\tm = &e.seenCantAddr\n\t}\n\tif _, ok := m.At(val.Type); ok {\n\t\treturn nil\n\t}\n\tm.Set(val.Type, struct{}{})\n\n\t// Drill into interfaces and pointers.\n\tseen := map[fakereflect.TypeAndCanAddr]struct{}{}\n\tfor val.IsInterface() || val.IsPtr() {\n\t\tif val.IsInterface() {\n\t\t\treturn nil\n\t\t}\n\t\tval = val.Elem()\n\t\tif _, ok := seen[val]; ok {\n\t\t\t// Loop in type graph, e.g. 'type P *P'\n\t\t\treturn &CyclicTypeError{val.Type, stack}\n\t\t}\n\t\tseen[val] = struct{}{}\n\t}\n\n\t// Check for marshaler.\n\tif implementsMarshaler(val) {\n\t\treturn nil\n\t}\n\tif val.CanAddr() {\n\t\tpv := fakereflect.PtrTo(val)\n\t\tif implementsMarshaler(pv) {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// Check for text marshaler.\n\tif val.Implements(knowledge.Interfaces[\"encoding.TextMarshaler\"]) {\n\t\treturn nil\n\t}\n\tif val.CanAddr() {\n\t\tpv := fakereflect.PtrTo(val)\n\t\tif pv.Implements(knowledge.Interfaces[\"encoding.TextMarshaler\"]) {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// Slices and arrays iterate over the elements. They do not have an enclosing tag.\n\tif (val.IsSlice() || val.IsArray()) && !isByteArray(val) && !isByteSlice(val) {\n\t\tif err := e.marshalValue(val.Elem(), finfo, startTemplate, stack+\"[0]\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\n\ttinfo, err := getTypeInfo(val)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Create start element.\n\t// Precedence for the XML element name is:\n\t// 0. startTemplate\n\t// 1. XMLName field in underlying struct;\n\t// 2. field name/tag in the struct field; and\n\t// 3. type name\n\tvar start StartElement\n\n\tif startTemplate != nil {\n\t\tstart.Name = startTemplate.Name\n\t\tstart.Attr = append(start.Attr, startTemplate.Attr...)\n\t} else if tinfo.xmlname != nil {\n\t\txmlname := tinfo.xmlname\n\t\tif xmlname.name != \"\" {\n\t\t\tstart.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name\n\t\t}\n\t}\n\n\t// Attributes\n\tfor i := range tinfo.fields {\n\t\tfinfo := &tinfo.fields[i]\n\t\tif finfo.flags&fAttr == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfv := finfo.value(val)\n\n\t\tname := Name{Space: finfo.xmlns, Local: finfo.name}\n\t\tif err := e.marshalAttr(&start, name, fv, stack+pathByIndex(val, finfo.idx)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif val.IsStruct() {\n\t\treturn e.marshalStruct(tinfo, val, stack)\n\t} else {\n\t\treturn e.marshalSimple(val, stack)\n\t}\n}\n\nfunc isSlice(v fakereflect.TypeAndCanAddr) bool {\n\t_, ok := v.Type.Underlying().(*types.Slice)\n\treturn ok\n}\n\nfunc isByteSlice(v fakereflect.TypeAndCanAddr) bool {\n\tslice, ok := v.Type.Underlying().(*types.Slice)\n\tif !ok {\n\t\treturn false\n\t}\n\tbasic, ok := slice.Elem().Underlying().(*types.Basic)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn basic.Kind() == types.Uint8\n}\n\nfunc isByteArray(v fakereflect.TypeAndCanAddr) bool {\n\tslice, ok := v.Type.Underlying().(*types.Array)\n\tif !ok {\n\t\treturn false\n\t}\n\tbasic, ok := slice.Elem().Underlying().(*types.Basic)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn basic.Kind() == types.Uint8\n}\n\n// marshalAttr marshals an attribute with the given name and value, adding to start.Attr.\nfunc (e *Encoder) marshalAttr(start *StartElement, name Name, val fakereflect.TypeAndCanAddr, stack string) error {\n\tif implementsMarshalerAttr(val) {\n\t\treturn nil\n\t}\n\n\tif val.CanAddr() {\n\t\tpv := fakereflect.PtrTo(val)\n\t\tif implementsMarshalerAttr(pv) {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tif val.Implements(knowledge.Interfaces[\"encoding.TextMarshaler\"]) {\n\t\treturn nil\n\t}\n\n\tif val.CanAddr() {\n\t\tpv := fakereflect.PtrTo(val)\n\t\tif pv.Implements(knowledge.Interfaces[\"encoding.TextMarshaler\"]) {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// Dereference or skip nil pointer\n\tif val.IsPtr() {\n\t\tval = val.Elem()\n\t}\n\n\t// Walk slices.\n\tif isSlice(val) && !isByteSlice(val) {\n\t\tif err := e.marshalAttr(start, name, val.Elem(), stack+\"[0]\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\n\tif typeutil.IsTypeWithName(val.Type, \"encoding/xml.Attr\") {\n\t\treturn nil\n\t}\n\n\treturn e.marshalSimple(val, stack)\n}\n\nfunc (e *Encoder) marshalSimple(val fakereflect.TypeAndCanAddr, stack string) error {\n\tswitch val.Type.Underlying().(type) {\n\tcase *types.Basic, *types.Interface:\n\t\treturn nil\n\tcase *types.Slice, *types.Array:\n\t\tbasic, ok := val.Elem().Type.Underlying().(*types.Basic)\n\t\tif !ok || basic.Kind() != types.Uint8 {\n\t\t\treturn &UnsupportedTypeError{val.Type, stack}\n\t\t}\n\t\treturn nil\n\tdefault:\n\t\treturn &UnsupportedTypeError{val.Type, stack}\n\t}\n}\n\nfunc indirect(vf fakereflect.TypeAndCanAddr) fakereflect.TypeAndCanAddr {\n\tfor vf.IsPtr() {\n\t\tvf = vf.Elem()\n\t}\n\treturn vf\n}\n\nfunc pathByIndex(t fakereflect.TypeAndCanAddr, index []int) string {\n\tvar path strings.Builder\n\tfor _, i := range index {\n\t\tif t.IsPtr() {\n\t\t\tt = t.Elem()\n\t\t}\n\t\tpath.WriteString(\".\" + t.Field(i).Name)\n\t\tt = t.Field(i).Type\n\t}\n\treturn path.String()\n}\n\nfunc (e *Encoder) marshalStruct(tinfo *typeInfo, val fakereflect.TypeAndCanAddr, stack string) error {\n\tfor i := range tinfo.fields {\n\t\tfinfo := &tinfo.fields[i]\n\t\tif finfo.flags&fAttr != 0 {\n\t\t\tcontinue\n\t\t}\n\t\tvf := finfo.value(val)\n\n\t\tswitch finfo.flags & fMode {\n\t\tcase fCDATA, fCharData:\n\t\t\tif vf.Implements(knowledge.Interfaces[\"encoding.TextMarshaler\"]) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif vf.CanAddr() {\n\t\t\t\tpv := fakereflect.PtrTo(vf)\n\t\t\t\tif pv.Implements(knowledge.Interfaces[\"encoding.TextMarshaler\"]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue\n\n\t\tcase fComment:\n\t\t\tvf = indirect(vf)\n\t\t\tif !(isByteSlice(vf) || isByteArray(vf)) {\n\t\t\t\treturn fmt.Errorf(\"xml: bad type for comment field of %s\", val)\n\t\t\t}\n\t\t\tcontinue\n\n\t\tcase fInnerXML:\n\t\t\tvf = indirect(vf)\n\t\t\tif t, ok := vf.Type.(*types.Slice); (ok && types.Identical(t.Elem(), types.Typ[types.Byte])) || types.Identical(vf.Type, types.Typ[types.String]) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\tcase fElement, fElement | fAny:\n\t\t}\n\t\tif err := e.marshalValue(vf, finfo, nil, stack+pathByIndex(val, finfo.idx)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// UnsupportedTypeError is returned when Marshal encounters a type\n// that cannot be converted into XML.\ntype UnsupportedTypeError struct {\n\tType types.Type\n\tPath string\n}\n\nfunc (e *UnsupportedTypeError) Error() string {\n\treturn fmt.Sprintf(\"xml: unsupported type %s, via %s \", e.Type, e.Path)\n}\n"
  },
  {
    "path": "staticcheck/fakexml/typeinfo.go",
    "content": "// Copyright 2011 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage fakexml\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/staticcheck/fakereflect\"\n)\n\n// typeInfo holds details for the xml representation of a type.\ntype typeInfo struct {\n\txmlname *fieldInfo\n\tfields  []fieldInfo\n}\n\n// fieldInfo holds details for the xml representation of a single field.\ntype fieldInfo struct {\n\tidx     []int\n\tname    string\n\txmlns   string\n\tflags   fieldFlags\n\tparents []string\n}\n\ntype fieldFlags int\n\nconst (\n\tfElement fieldFlags = 1 << iota\n\tfAttr\n\tfCDATA\n\tfCharData\n\tfInnerXML\n\tfComment\n\tfAny\n\n\tfOmitEmpty\n\n\tfMode = fElement | fAttr | fCDATA | fCharData | fInnerXML | fComment | fAny\n\n\txmlName = \"XMLName\"\n)\n\nfunc (f fieldFlags) String() string {\n\tswitch f {\n\tcase fAttr:\n\t\treturn \"attr\"\n\tcase fCDATA:\n\t\treturn \"cdata\"\n\tcase fCharData:\n\t\treturn \"chardata\"\n\tcase fInnerXML:\n\t\treturn \"innerxml\"\n\tcase fComment:\n\t\treturn \"comment\"\n\tcase fAny:\n\t\treturn \"any\"\n\tcase fOmitEmpty:\n\t\treturn \"omitempty\"\n\tcase fAny | fAttr:\n\t\treturn \"any,attr\"\n\tdefault:\n\t\treturn strconv.Itoa(int(f))\n\t}\n}\n\nvar tinfoMap sync.Map // map[reflect.Type]*typeInfo\n\n// getTypeInfo returns the typeInfo structure with details necessary\n// for marshaling and unmarshaling typ.\nfunc getTypeInfo(typ fakereflect.TypeAndCanAddr) (*typeInfo, error) {\n\tif ti, ok := tinfoMap.Load(typ); ok {\n\t\treturn ti.(*typeInfo), nil\n\t}\n\n\ttinfo := &typeInfo{}\n\tif typ.IsStruct() && !typeutil.IsTypeWithName(typ.Type, \"encoding/xml.Name\") {\n\t\tn := typ.NumField()\n\t\tfor i := range n {\n\t\t\tf := typ.Field(i)\n\t\t\tif (!f.IsExported() && !f.Anonymous) || f.Tag.Get(\"xml\") == \"-\" {\n\t\t\t\tcontinue // Private field\n\t\t\t}\n\n\t\t\t// For embedded structs, embed its fields.\n\t\t\tif f.Anonymous {\n\t\t\t\tt := f.Type\n\t\t\t\tif t.IsPtr() {\n\t\t\t\t\tt = t.Elem()\n\t\t\t\t}\n\t\t\t\tif t.IsStruct() {\n\t\t\t\t\tinner, err := getTypeInfo(t)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\tif tinfo.xmlname == nil {\n\t\t\t\t\t\ttinfo.xmlname = inner.xmlname\n\t\t\t\t\t}\n\t\t\t\t\tfor _, finfo := range inner.fields {\n\t\t\t\t\t\tfinfo.idx = append([]int{i}, finfo.idx...)\n\t\t\t\t\t\tif err := addFieldInfo(typ, tinfo, &finfo); err != nil {\n\t\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfinfo, err := StructFieldInfo(f)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tif f.Name == xmlName {\n\t\t\t\ttinfo.xmlname = finfo\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Add the field if it doesn't conflict with other fields.\n\t\t\tif err := addFieldInfo(typ, tinfo, finfo); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\n\tti, _ := tinfoMap.LoadOrStore(typ, tinfo)\n\treturn ti.(*typeInfo), nil\n}\n\n// StructFieldInfo builds and returns a fieldInfo for f.\nfunc StructFieldInfo(f fakereflect.StructField) (*fieldInfo, error) {\n\tfinfo := &fieldInfo{idx: f.Index}\n\n\t// Split the tag from the xml namespace if necessary.\n\ttag := f.Tag.Get(\"xml\")\n\tif i := strings.Index(tag, \" \"); i >= 0 {\n\t\tfinfo.xmlns, tag = tag[:i], tag[i+1:]\n\t}\n\n\t// Parse flags.\n\ttokens := strings.Split(tag, \",\")\n\tif len(tokens) == 1 {\n\t\tfinfo.flags = fElement\n\t} else {\n\t\ttag = tokens[0]\n\t\tfor _, flag := range tokens[1:] {\n\t\t\tswitch flag {\n\t\t\tcase \"attr\":\n\t\t\t\tfinfo.flags |= fAttr\n\t\t\tcase \"cdata\":\n\t\t\t\tfinfo.flags |= fCDATA\n\t\t\tcase \"chardata\":\n\t\t\t\tfinfo.flags |= fCharData\n\t\t\tcase \"innerxml\":\n\t\t\t\tfinfo.flags |= fInnerXML\n\t\t\tcase \"comment\":\n\t\t\t\tfinfo.flags |= fComment\n\t\t\tcase \"any\":\n\t\t\t\tfinfo.flags |= fAny\n\t\t\tcase \"omitempty\":\n\t\t\t\tfinfo.flags |= fOmitEmpty\n\t\t\t}\n\t\t}\n\n\t\t// Validate the flags used.\n\t\tswitch mode := finfo.flags & fMode; mode {\n\t\tcase 0:\n\t\t\tfinfo.flags |= fElement\n\t\tcase fAttr, fCDATA, fCharData, fInnerXML, fComment, fAny, fAny | fAttr:\n\t\t\tif f.Name == xmlName {\n\t\t\t\treturn nil, fmt.Errorf(\"cannot use option %s on XMLName field\", mode)\n\t\t\t} else if tag != \"\" && mode != fAttr {\n\t\t\t\treturn nil, fmt.Errorf(\"cannot specify name together with option ,%s\", mode)\n\t\t\t}\n\t\tdefault:\n\t\t\t// This will also catch multiple modes in a single field.\n\t\t\treturn nil, fmt.Errorf(\"invalid combination of options: %q\", f.Tag.Get(\"xml\"))\n\t\t}\n\t\tif finfo.flags&fMode == fAny {\n\t\t\tfinfo.flags |= fElement\n\t\t}\n\t\tif finfo.flags&fOmitEmpty != 0 && finfo.flags&(fElement|fAttr) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"can only use omitempty on elements and attributes\")\n\t\t}\n\t}\n\n\t// Use of xmlns without a name is not allowed.\n\tif finfo.xmlns != \"\" && tag == \"\" {\n\t\treturn nil, fmt.Errorf(\"namespace without name: %q\", f.Tag.Get(\"xml\"))\n\t}\n\n\tif f.Name == xmlName {\n\t\t// The XMLName field records the XML element name. Don't\n\t\t// process it as usual because its name should default to\n\t\t// empty rather than to the field name.\n\t\tfinfo.name = tag\n\t\treturn finfo, nil\n\t}\n\n\tif tag == \"\" {\n\t\t// If the name part of the tag is completely empty, get\n\t\t// default from XMLName of underlying struct if feasible,\n\t\t// or field name otherwise.\n\t\tif xmlname := lookupXMLName(f.Type); xmlname != nil {\n\t\t\tfinfo.xmlns, finfo.name = xmlname.xmlns, xmlname.name\n\t\t} else {\n\t\t\tfinfo.name = f.Name\n\t\t}\n\t\treturn finfo, nil\n\t}\n\n\t// Prepare field name and parents.\n\tparents := strings.Split(tag, \">\")\n\tif parents[0] == \"\" {\n\t\tparents[0] = f.Name\n\t}\n\tif parents[len(parents)-1] == \"\" {\n\t\treturn nil, fmt.Errorf(\"trailing '>'\")\n\t}\n\tfinfo.name = parents[len(parents)-1]\n\tif len(parents) > 1 {\n\t\tif (finfo.flags & fElement) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"%s chain not valid with %s flag\", tag, strings.Join(tokens[1:], \",\"))\n\t\t}\n\t\tfinfo.parents = parents[:len(parents)-1]\n\t}\n\n\t// If the field type has an XMLName field, the names must match\n\t// so that the behavior of both marshaling and unmarshaling\n\t// is straightforward and unambiguous.\n\tif finfo.flags&fElement != 0 {\n\t\tftyp := f.Type\n\t\txmlname := lookupXMLName(ftyp)\n\t\tif xmlname != nil && xmlname.name != finfo.name {\n\t\t\treturn nil, fmt.Errorf(\"name %q conflicts with name %q in %s.XMLName\", finfo.name, xmlname.name, ftyp)\n\t\t}\n\t}\n\treturn finfo, nil\n}\n\n// lookupXMLName returns the fieldInfo for typ's XMLName field\n// in case it exists and has a valid xml field tag, otherwise\n// it returns nil.\nfunc lookupXMLName(typ fakereflect.TypeAndCanAddr) (xmlname *fieldInfo) {\n\tseen := map[fakereflect.TypeAndCanAddr]struct{}{}\n\tfor typ.IsPtr() {\n\t\ttyp = typ.Elem()\n\t\tif _, ok := seen[typ]; ok {\n\t\t\t// Loop in type graph, e.g. 'type P *P'\n\t\t\treturn nil\n\t\t}\n\t\tseen[typ] = struct{}{}\n\t}\n\tif !typ.IsStruct() {\n\t\treturn nil\n\t}\n\tfor i, n := 0, typ.NumField(); i < n; i++ {\n\t\tf := typ.Field(i)\n\t\tif f.Name != xmlName {\n\t\t\tcontinue\n\t\t}\n\t\tfinfo, err := StructFieldInfo(f)\n\t\tif err == nil && finfo.name != \"\" {\n\t\t\treturn finfo\n\t\t}\n\t\t// Also consider errors as a non-existent field tag\n\t\t// and let getTypeInfo itself report the error.\n\t\tbreak\n\t}\n\treturn nil\n}\n\n// addFieldInfo adds finfo to tinfo.fields if there are no\n// conflicts, or if conflicts arise from previous fields that were\n// obtained from deeper embedded structures than finfo. In the latter\n// case, the conflicting entries are dropped.\n// A conflict occurs when the path (parent + name) to a field is\n// itself a prefix of another path, or when two paths match exactly.\n// It is okay for field paths to share a common, shorter prefix.\nfunc addFieldInfo(typ fakereflect.TypeAndCanAddr, tinfo *typeInfo, newf *fieldInfo) error {\n\tvar conflicts []int\nLoop:\n\t// First, figure all conflicts. Most working code will have none.\n\tfor i := range tinfo.fields {\n\t\toldf := &tinfo.fields[i]\n\t\tif oldf.flags&fMode != newf.flags&fMode {\n\t\t\tcontinue\n\t\t}\n\t\tif oldf.xmlns != \"\" && newf.xmlns != \"\" && oldf.xmlns != newf.xmlns {\n\t\t\tcontinue\n\t\t}\n\t\tminl := min(len(newf.parents), len(oldf.parents))\n\t\tfor p := range minl {\n\t\t\tif oldf.parents[p] != newf.parents[p] {\n\t\t\t\tcontinue Loop\n\t\t\t}\n\t\t}\n\t\tif len(oldf.parents) > len(newf.parents) {\n\t\t\tif oldf.parents[len(newf.parents)] == newf.name {\n\t\t\t\tconflicts = append(conflicts, i)\n\t\t\t}\n\t\t} else if len(oldf.parents) < len(newf.parents) {\n\t\t\tif newf.parents[len(oldf.parents)] == oldf.name {\n\t\t\t\tconflicts = append(conflicts, i)\n\t\t\t}\n\t\t} else {\n\t\t\tif newf.name == oldf.name {\n\t\t\t\tconflicts = append(conflicts, i)\n\t\t\t}\n\t\t}\n\t}\n\t// Without conflicts, add the new field and return.\n\tif conflicts == nil {\n\t\ttinfo.fields = append(tinfo.fields, *newf)\n\t\treturn nil\n\t}\n\n\t// If any conflict is shallower, ignore the new field.\n\t// This matches the Go field resolution on embedding.\n\tfor _, i := range conflicts {\n\t\tif len(tinfo.fields[i].idx) < len(newf.idx) {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// Otherwise, if any of them is at the same depth level, it's an error.\n\tfor _, i := range conflicts {\n\t\toldf := &tinfo.fields[i]\n\t\tif len(oldf.idx) == len(newf.idx) {\n\t\t\tf1 := typ.FieldByIndex(oldf.idx)\n\t\t\tf2 := typ.FieldByIndex(newf.idx)\n\t\t\treturn &TagPathError{typ, f1.Name, f1.Tag.Get(\"xml\"), f2.Name, f2.Tag.Get(\"xml\")}\n\t\t}\n\t}\n\n\t// Otherwise, the new field is shallower, and thus takes precedence,\n\t// so drop the conflicting fields from tinfo and append the new one.\n\tfor c := len(conflicts) - 1; c >= 0; c-- {\n\t\ti := conflicts[c]\n\t\tcopy(tinfo.fields[i:], tinfo.fields[i+1:])\n\t\ttinfo.fields = tinfo.fields[:len(tinfo.fields)-1]\n\t}\n\ttinfo.fields = append(tinfo.fields, *newf)\n\treturn nil\n}\n\n// A TagPathError represents an error in the unmarshaling process\n// caused by the use of field tags with conflicting paths.\ntype TagPathError struct {\n\tStruct       fakereflect.TypeAndCanAddr\n\tField1, Tag1 string\n\tField2, Tag2 string\n}\n\nfunc (e *TagPathError) Error() string {\n\treturn fmt.Sprintf(\"%s field %q with tag %q conflicts with field %q with tag %q\", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)\n}\n\n// value returns v's field value corresponding to finfo.\n// It's equivalent to v.FieldByIndex(finfo.idx), but when passed\n// initNilPointers, it initializes and dereferences pointers as necessary.\n// When passed dontInitNilPointers and a nil pointer is reached, the function\n// returns a zero reflect.Value.\nfunc (finfo *fieldInfo) value(v fakereflect.TypeAndCanAddr) fakereflect.TypeAndCanAddr {\n\tfor i, x := range finfo.idx {\n\t\tif i > 0 {\n\t\t\tt := v\n\t\t\tif t.IsPtr() && t.Elem().IsStruct() {\n\t\t\t\tv = v.Elem()\n\t\t\t}\n\t\t}\n\t\tv = v.Field(x).Type\n\t}\n\treturn v\n}\n"
  },
  {
    "path": "staticcheck/fakexml/xml.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage fakexml\n\n// References:\n//    Annotated XML spec: https://www.xml.com/axml/testaxml.htm\n//    XML name spaces: https://www.w3.org/TR/REC-xml-names/\n\n// TODO(rsc):\n//\tTest error handling.\n\n// A Name represents an XML name (Local) annotated\n// with a name space identifier (Space).\n// In tokens returned by Decoder.Token, the Space identifier\n// is given as a canonical URL, not the short prefix used\n// in the document being parsed.\ntype Name struct {\n\tSpace, Local string\n}\n\n// An Attr represents an attribute in an XML element (Name=Value).\ntype Attr struct {\n\tName  Name\n\tValue string\n}\n\n// A StartElement represents an XML start element.\ntype StartElement struct {\n\tName Name\n\tAttr []Attr\n}\n"
  },
  {
    "path": "staticcheck/sa1000/sa1000.go",
    "content": "package sa1000\n\nimport (\n\t\"go/constant\"\n\t\"regexp\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1000\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Invalid regular expression`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"regexp.MustCompile\": check,\n\t\"regexp.Compile\":     check,\n\t\"regexp.Match\":       check,\n\t\"regexp.MatchReader\": check,\n\t\"regexp.MatchString\": check,\n}\n\nfunc check(call *callcheck.Call) {\n\targ := call.Args[0]\n\tif c := callcheck.ExtractConstExpectKind(arg.Value, constant.String); c != nil {\n\t\ts := constant.StringVal(c.Value)\n\t\tif _, err := regexp.Compile(s); err != nil {\n\t\t\targ.Invalid(err.Error())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1000/sa1000_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1000\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1000/testdata/go1.0/CheckRegexps/CheckRegexps.go",
    "content": "package pkg\n\nimport (\n\t\"log\"\n\t\"regexp\"\n)\n\nconst c1 = `[`\nconst c2 = `(abc)`\n\nvar re1 = regexp.MustCompile(`ab\\yef`) //@ diag(`error parsing regexp`)\nvar re2 = regexp.MustCompile(c1)       //@ diag(`error parsing regexp`)\nvar re3 = regexp.MustCompile(c2)\nvar re4 = regexp.MustCompile(\n\tc1, //@ diag(`error parsing regexp`)\n)\n\nfunc fn() {\n\t_, err := regexp.Compile(`foo(`) //@ diag(`error parsing regexp`)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tif re2.MatchString(\"foo(\") {\n\t\tlog.Println(\"of course 'foo(' matches 'foo('\")\n\t}\n\n\tregexp.Match(\"foo(\", nil)       //@ diag(`error parsing regexp`)\n\tregexp.MatchReader(\"foo(\", nil) //@ diag(`error parsing regexp`)\n\tregexp.MatchString(\"foo(\", \"\")  //@ diag(`error parsing regexp`)\n}\n\n// must be a basic type to trigger SA4017 (in case of a test failure)\ntype T string\n\nfunc (T) Fn() {}\n\n// Don't get confused by methods named init\nfunc (T) init() {}\n\n// this will become a synthetic init function, that we don't want to\n// ignore\nvar _ = regexp.MustCompile(\"(\") //@ diag(`error parsing regexp`)\n\nfunc fn2() {\n\tregexp.MustCompile(\"foo(\").FindAll(nil, 0) //@ diag(`error parsing regexp`)\n}\n"
  },
  {
    "path": "staticcheck/sa1001/sa1001.go",
    "content": "package sa1001\n\nimport (\n\t\"go/ast\"\n\thtmltemplate \"html/template\"\n\t\"strings\"\n\ttexttemplate \"text/template\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/knowledge\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1001\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Invalid template`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar query = pattern.MustParse(`\n\t(CallExpr\n\t\t(Symbol\n\t\tname@(Or\n\t\t\t\"(*text/template.Template).Parse\"\n\t\t\t\"(*html/template.Template).Parse\"))\n\t\t[s])`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, query) {\n\t\tname := m.State[\"name\"].(string)\n\t\tvar kind string\n\t\tswitch name {\n\t\tcase \"(*text/template.Template).Parse\":\n\t\t\tkind = \"text\"\n\t\tcase \"(*html/template.Template).Parse\":\n\t\t\tkind = \"html\"\n\t\t}\n\n\t\tcall := node.(*ast.CallExpr)\n\t\tsel := call.Fun.(*ast.SelectorExpr)\n\t\tif !code.IsCallToAny(pass, sel.X, \"text/template.New\", \"html/template.New\") {\n\t\t\t// TODO(dh): this is a cheap workaround for templates with\n\t\t\t// different delims. A better solution with less false\n\t\t\t// negatives would use data flow analysis to see where the\n\t\t\t// template comes from and where it has been\n\t\t\tcontinue\n\t\t}\n\n\t\ts, ok := code.ExprToString(pass, m.State[\"s\"].(ast.Expr))\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tvar err error\n\t\tswitch kind {\n\t\tcase \"text\":\n\t\t\t_, err = texttemplate.New(\"\").Parse(s)\n\t\tcase \"html\":\n\t\t\t_, err = htmltemplate.New(\"\").Parse(s)\n\t\t}\n\t\tif err != nil {\n\t\t\t// TODO(dominikh): whitelist other parse errors, if any\n\t\t\tif strings.Contains(err.Error(), \"unexpected\") ||\n\t\t\t\tstrings.Contains(err.Error(), \"bad character\") {\n\t\t\t\treport.Report(pass, call.Args[knowledge.Arg(\"(*text/template.Template).Parse.text\")], err.Error())\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1001/sa1001_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1001\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1001/testdata/go1.0/CheckTemplate/CheckTemplate.go",
    "content": "package pkg\n\nimport (\n\tth \"html/template\"\n\ttt \"text/template\"\n)\n\nconst tmpl1 = `{{.Name}} {{.LastName}`\nconst tmpl2 = `{{fn}}`\n\nfunc fn() {\n\ttt.New(\"\").Parse(tmpl1) //@ diag(`template`)\n\ttt.New(\"\").Parse(tmpl2)\n\tt1 := tt.New(\"\")\n\tt1.Parse(tmpl1)\n\tth.New(\"\").Parse(tmpl1) //@ diag(`template`)\n\tth.New(\"\").Parse(tmpl2)\n\tt2 := th.New(\"\")\n\tt2.Parse(tmpl1)\n\ttt.New(\"\").Delims(\"[[\", \"]]\").Parse(\"{{abc-}}\")\n}\n"
  },
  {
    "path": "staticcheck/sa1002/sa1002.go",
    "content": "package sa1002\n\nimport (\n\t\"go/constant\"\n\t\"strings\"\n\t\"time\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1002\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Invalid format in \\'time.Parse\\'`,\n\t\tText: `\\'time.Parse\\' requires a layout string that uses Go's reference time:\n\\\"Mon Jan 2 15:04:05 MST 2006\\\". The layout must represent this date and time\nexactly. See https://pkg.go.dev/time#pkg-constants for layout examples.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"time.Parse\": func(call *callcheck.Call) {\n\t\targ := call.Args[knowledge.Arg(\"time.Parse.layout\")]\n\t\tif c := callcheck.ExtractConstExpectKind(arg.Value, constant.String); c != nil {\n\t\t\ts := constant.StringVal(c.Value)\n\t\t\ts = strings.Replace(s, \"_\", \" \", -1)\n\t\t\ts = strings.Replace(s, \"Z\", \"-\", -1)\n\t\t\t_, err := time.Parse(s, s)\n\t\t\tif err != nil {\n\t\t\t\targ.Invalid(err.Error())\n\t\t\t}\n\t\t}\n\t},\n}\n"
  },
  {
    "path": "staticcheck/sa1002/sa1002_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1002\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1002/testdata/go1.0/CheckTimeParse/CheckTimeParse.go",
    "content": "package pkg\n\nimport \"time\"\n\nconst c1 = \"12345\"\nconst c2 = \"2006\"\n\nfunc fn() {\n\ttime.Parse(\"12345\", \"\") //@ diag(`parsing time`)\n\ttime.Parse(c1, \"\")      //@ diag(`parsing time`)\n\ttime.Parse(c2, \"\")\n\ttime.Parse(time.RFC3339Nano, \"\")\n\ttime.Parse(time.Kitchen, \"\")\n}\n"
  },
  {
    "path": "staticcheck/sa1003/sa1003.go",
    "content": "package sa1003\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\t\"go/version\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1003\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(checkEncodingBinaryRules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Unsupported argument to functions in \\'encoding/binary\\'`,\n\t\tText: `The \\'encoding/binary\\' package can only serialize types with known sizes.\nThis precludes the use of the \\'int\\' and \\'uint\\' types, as their sizes\ndiffer on different architectures. Furthermore, it doesn't support\nserializing maps, channels, strings, or functions.\n\nBefore Go 1.8, \\'bool\\' wasn't supported, either.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkEncodingBinaryRules = map[string]callcheck.Check{\n\t\"encoding/binary.Write\": func(call *callcheck.Call) {\n\t\targ := call.Args[knowledge.Arg(\"encoding/binary.Write.data\")]\n\t\tif !CanBinaryMarshal(call.Pass, call.Parent, arg.Value) {\n\t\t\targ.Invalid(fmt.Sprintf(\"value of type %s cannot be used with binary.Write\", arg.Value.Value.Type()))\n\t\t}\n\t},\n}\n\nfunc CanBinaryMarshal(pass *analysis.Pass, node code.Positioner, v callcheck.Value) bool {\n\ttyp := v.Value.Type().Underlying()\n\tif ttyp, ok := typ.(*types.Pointer); ok {\n\t\ttyp = ttyp.Elem().Underlying()\n\t}\n\tif ttyp, ok := types.Unalias(typ).(interface {\n\t\tElem() types.Type\n\t}); ok {\n\t\tif _, ok := ttyp.(*types.Pointer); !ok {\n\t\t\ttyp = ttyp.Elem()\n\t\t}\n\t}\n\n\treturn validEncodingBinaryType(pass, node, typ)\n}\n\nfunc validEncodingBinaryType(pass *analysis.Pass, node code.Positioner, typ types.Type) bool {\n\ttyp = typ.Underlying()\n\tswitch typ := typ.(type) {\n\tcase *types.Basic:\n\t\tswitch typ.Kind() {\n\t\tcase types.Uint8, types.Uint16, types.Uint32, types.Uint64,\n\t\t\ttypes.Int8, types.Int16, types.Int32, types.Int64,\n\t\t\ttypes.Float32, types.Float64, types.Complex64, types.Complex128, types.Invalid:\n\t\t\treturn true\n\t\tcase types.Bool:\n\t\t\treturn version.Compare(code.StdlibVersion(pass, node), \"go1.8\") >= 0\n\t\t}\n\t\treturn false\n\tcase *types.Struct:\n\t\tn := typ.NumFields()\n\t\tfor i := range n {\n\t\t\tif !validEncodingBinaryType(pass, node, typ.Field(i).Type()) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\tcase *types.Array:\n\t\treturn validEncodingBinaryType(pass, node, typ.Elem())\n\tcase *types.Interface:\n\t\t// we can't determine if it's a valid type or not\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "staticcheck/sa1003/sa1003_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1003\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1003/testdata/go1.0/CheckEncodingBinary/CheckEncodingBinary.go",
    "content": "package pkg\n\nimport (\n\t\"encoding/binary\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n)\n\nfunc fn() {\n\ttype T1 struct {\n\t\tA int32\n\t}\n\ttype T2 struct {\n\t\tA int32\n\t\tB int\n\t}\n\ttype T3 struct {\n\t\tA []int32\n\t}\n\ttype T4 struct {\n\t\tA *int32\n\t}\n\ttype T5 struct {\n\t\tA int32\n\t}\n\ttype T6 []byte\n\n\tvar x1 int\n\tvar x2 int32\n\tvar x3 []int\n\tvar x4 []int32\n\tvar x5 [1]int\n\tvar x6 [1]int32\n\tvar x7 T1\n\tvar x8 T2\n\tvar x9 T3\n\tvar x10 T4\n\tvar x11 = &T5{}\n\tvar x13 []byte\n\tvar x14 *[]byte\n\tvar x15 T6\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x1)) //@ diag(`cannot be used with binary.Write`)\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x2))\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x3)) //@ diag(`cannot be used with binary.Write`)\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x4))\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x5)) //@ diag(`cannot be used with binary.Write`)\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x6))\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x7))\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x8))  //@ diag(`cannot be used with binary.Write`)\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x9))  //@ diag(`cannot be used with binary.Write`)\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x10)) //@ diag(`cannot be used with binary.Write`)\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x11))\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, &x13))\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, &x14)) //@ diag(`cannot be used with binary.Write`)\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x15))\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, &x15))\n\tlog.Println(binary.Write(fn2())) //@ diag(`cannot be used with binary.Write`)\n\tlog.Println(binary.Write(fn3()))\n}\n\nfunc fn2() (io.Writer, binary.ByteOrder, int)    { return nil, binary.LittleEndian, 0 }\nfunc fn3() (io.Writer, binary.ByteOrder, uint32) { return nil, binary.LittleEndian, 0 }\n"
  },
  {
    "path": "staticcheck/sa1003/testdata/go1.7/CheckEncodingBinary/CheckEncodingBinary.go",
    "content": "package pkg\n\nimport (\n\t\"encoding/binary\"\n\t\"io/ioutil\"\n\t\"log\"\n)\n\nfunc fn() {\n\tvar x bool\n\tlog.Println(binary.Write(ioutil.Discard, binary.LittleEndian, x)) //@ diag(`cannot be used with binary.Write`)\n}\n"
  },
  {
    "path": "staticcheck/sa1003/testdata/go1.8/CheckEncodingBinary/CheckEncodingBinary.go",
    "content": "package pkg\n\nimport (\n\t\"encoding/binary\"\n\t\"io/ioutil\"\n)\n\nfunc fn() {\n\tvar x bool\n\tbinary.Write(ioutil.Discard, binary.LittleEndian, x)\n}\n"
  },
  {
    "path": "staticcheck/sa1004/sa1004.go",
    "content": "package sa1004\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1004\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Suspiciously small untyped constant in \\'time.Sleep\\'`,\n\t\tText: `The \\'time\\'.Sleep function takes a \\'time.Duration\\' as its only argument.\nDurations are expressed in nanoseconds. Thus, calling \\'time.Sleep(1)\\'\nwill sleep for 1 nanosecond. This is a common source of bugs, as sleep\nfunctions in other languages often accept seconds or milliseconds.\n\nThe \\'time\\' package provides constants such as \\'time.Second\\' to express\nlarge durations. These can be combined with arithmetic to express\narbitrary durations, for example \\'5 * time.Second\\' for 5 seconds.\n\nIf you truly meant to sleep for a tiny amount of time, use\n\\'n * time.Nanosecond\\' to signal to Staticcheck that you did mean to sleep\nfor some amount of nanoseconds.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckTimeSleepConstantPatternQ   = pattern.MustParse(`(CallExpr (Symbol \"time.Sleep\") lit@(IntegerLiteral value))`)\n\tcheckTimeSleepConstantPatternRns = pattern.MustParse(`(BinaryExpr duration \"*\" (SelectorExpr (Ident \"time\") (Ident \"Nanosecond\")))`)\n\tcheckTimeSleepConstantPatternRs  = pattern.MustParse(`(BinaryExpr duration \"*\" (SelectorExpr (Ident \"time\") (Ident \"Second\")))`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, m := range code.Matches(pass, checkTimeSleepConstantPatternQ) {\n\t\tn, ok := constant.Int64Val(m.State[\"value\"].(types.TypeAndValue).Value)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tif n == 0 || n > 120 {\n\t\t\t// time.Sleep(0) is a seldom used pattern in concurrency\n\t\t\t// tests. >120 might be intentional. 120 was chosen\n\t\t\t// because the user could've meant 2 minutes.\n\t\t\tcontinue\n\t\t}\n\n\t\tlit := m.State[\"lit\"].(ast.Node)\n\t\treport.Report(pass, lit,\n\t\t\tfmt.Sprintf(\"sleeping for %d nanoseconds is probably a bug; be explicit if it isn't\", n), report.Fixes(\n\t\t\t\tedit.Fix(\"Explicitly use nanoseconds\", edit.ReplaceWithPattern(pass.Fset, lit, checkTimeSleepConstantPatternRns, pattern.State{\"duration\": lit})),\n\t\t\t\tedit.Fix(\"Use seconds\", edit.ReplaceWithPattern(pass.Fset, lit, checkTimeSleepConstantPatternRs, pattern.State{\"duration\": lit}))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1004/sa1004_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1004\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1004/testdata/go1.0/CheckTimeSleepConstant/CheckTimeSleepConstant.go",
    "content": "package pkg\n\nimport \"time\"\n\nconst c1 = 1\nconst c2 = 200\n\nfunc fn() {\n\ttime.Sleep(1)  //@ diag(`sleeping for 1`)\n\ttime.Sleep(42) //@ diag(`sleeping for 42`)\n\ttime.Sleep(201)\n\ttime.Sleep(c1)\n\ttime.Sleep(c2)\n\ttime.Sleep(2 * time.Nanosecond)\n\ttime.Sleep(time.Nanosecond)\n}\n"
  },
  {
    "path": "staticcheck/sa1004/testdata/go1.0/CheckTimeSleepConstant/CheckTimeSleepConstant.go.golden",
    "content": "-- Explicitly use nanoseconds --\npackage pkg\n\nimport \"time\"\n\nconst c1 = 1\nconst c2 = 200\n\nfunc fn() {\n\ttime.Sleep(1 * time.Nanosecond)  //@ diag(`sleeping for 1`)\n\ttime.Sleep(42 * time.Nanosecond) //@ diag(`sleeping for 42`)\n\ttime.Sleep(201)\n\ttime.Sleep(c1)\n\ttime.Sleep(c2)\n\ttime.Sleep(2 * time.Nanosecond)\n\ttime.Sleep(time.Nanosecond)\n}\n\n-- Use seconds --\npackage pkg\n\nimport \"time\"\n\nconst c1 = 1\nconst c2 = 200\n\nfunc fn() {\n\ttime.Sleep(1 * time.Second)  //@ diag(`sleeping for 1`)\n\ttime.Sleep(42 * time.Second) //@ diag(`sleeping for 42`)\n\ttime.Sleep(201)\n\ttime.Sleep(c1)\n\ttime.Sleep(c2)\n\ttime.Sleep(2 * time.Nanosecond)\n\ttime.Sleep(time.Nanosecond)\n}\n"
  },
  {
    "path": "staticcheck/sa1005/sa1005.go",
    "content": "package sa1005\n\nimport (\n\t\"go/ast\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1005\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Invalid first argument to \\'exec.Command\\'`,\n\t\tText: `\\'os/exec\\' runs programs directly (using variants of the fork and exec\nsystem calls on Unix systems). This shouldn't be confused with running\na command in a shell. The shell will allow for features such as input\nredirection, pipes, and general scripting. The shell is also\nresponsible for splitting the user's input into a program name and its\narguments. For example, the equivalent to\n\n    ls / /tmp\n\nwould be\n\n    exec.Command(\"ls\", \"/\", \"/tmp\")\n\nIf you want to run a command in a shell, consider using something like\nthe following – but be aware that not all systems, particularly\nWindows, will have a \\'/bin/sh\\' program:\n\n    exec.Command(\"/bin/sh\", \"-c\", \"ls | grep Awesome\")`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar query = pattern.MustParse(`(CallExpr (Symbol \"os/exec.Command\") arg1:_)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, m := range code.Matches(pass, query) {\n\t\targ1 := m.State[\"arg1\"].(ast.Expr)\n\t\tval, ok := code.ExprToString(pass, arg1)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tif !strings.Contains(val, \" \") || strings.Contains(val, `\\`) || strings.Contains(val, \"/\") {\n\t\t\tcontinue\n\t\t}\n\t\treport.Report(pass, arg1,\n\t\t\t\"first argument to exec.Command looks like a shell command, but a program name or path are expected\")\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1005/sa1005_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1005\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1005/testdata/go1.0/CheckExec/CheckExec.go",
    "content": "package pkg\n\nimport \"os/exec\"\n\nfunc fn() {\n\texec.Command(\"ls\")\n\texec.Command(\"ls arg1\") //@ diag(`first argument to exec`)\n\texec.Command(`C:\\Program Files\\this\\is\\insane.exe`)\n\texec.Command(\"/Library/Application Support/VMware Tools/vmware-tools-daemon\")\n}\n"
  },
  {
    "path": "staticcheck/sa1006/sa1006.go",
    "content": "package sa1006\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1006\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `\\'Printf\\' with dynamic first argument and no further arguments`,\n\t\tText: `Using \\'fmt.Printf\\' with a dynamic first argument can lead to unexpected\noutput. The first argument is a format string, where certain character\ncombinations have special meaning. If, for example, a user were to\nenter a string such as\n\n    Interest rate: 5%\n\nand you printed it with\n\n    fmt.Printf(s)\n\nit would lead to the following output:\n\n    Interest rate: 5%!(NOVERB).\n\nSimilarly, forming the first parameter via string concatenation with\nuser input should be avoided for the same reason. When printing user\ninput, either use a variant of \\'fmt.Print\\', or use the \\'%s\\' Printf verb\nand pass the string as an argument.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar query1 = pattern.MustParse(`\n\t(CallExpr\n\t\t(Symbol\n\t\t\tname@(Or\n\t\t\t\t\"fmt.Errorf\"\n\t\t\t\t\"fmt.Printf\"\n\t\t\t\t\"fmt.Sprintf\"\n\t\t\t\t\"log.Fatalf\"\n\t\t\t\t\"log.Panicf\"\n\t\t\t\t\"log.Printf\"\n\t\t\t\t\"(*log.Logger).Printf\"\n\t\t\t\t\"(*testing.common).Logf\"\n\t\t\t\t\"(*testing.common).Errorf\"\n\t\t\t\t\"(*testing.common).Fatalf\"\n\t\t\t\t\"(*testing.common).Skipf\"\n\t\t\t\t\"(testing.TB).Logf\"\n\t\t\t\t\"(testing.TB).Errorf\"\n\t\t\t\t\"(testing.TB).Fatalf\"\n\t\t\t\t\"(testing.TB).Skipf\"))\n\t\tformat:[])\n`)\n\nvar query2 = pattern.MustParse(`(CallExpr (Symbol \"fmt.Fprintf\") _:format:[])`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, query1, query2) {\n\t\tcall := node.(*ast.CallExpr)\n\t\tname, ok := m.State[\"name\"].(string)\n\t\tif !ok {\n\t\t\tname = \"fmt.Fprintf\"\n\t\t}\n\n\t\targ := m.State[\"format\"].(ast.Expr)\n\t\tswitch arg.(type) {\n\t\tcase *ast.CallExpr, *ast.Ident:\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, ok := pass.TypesInfo.TypeOf(arg).(*types.Tuple); ok {\n\t\t\t// the called function returns multiple values and got\n\t\t\t// splatted into the call. for all we know, it is\n\t\t\t// returning good arguments.\n\t\t\tcontinue\n\t\t}\n\n\t\tvar alt string\n\t\tif name == \"fmt.Errorf\" {\n\t\t\t// The alternative to fmt.Errorf isn't fmt.Error but errors.New\n\t\t\talt = \"errors.New\"\n\t\t} else {\n\t\t\t// This can be either a function call like log.Printf or a method call with an\n\t\t\t// arbitrarily complex selector, such as foo.bar[0].Printf. In either case,\n\t\t\t// all we have to do is remove the final 'f' from the existing call.Fun\n\t\t\t// expression.\n\t\t\talt = report.Render(pass, call.Fun)\n\t\t\talt = alt[:len(alt)-1]\n\t\t}\n\t\treport.Report(pass, call,\n\t\t\t\"printf-style function with dynamic format string and no further arguments should use print-style function instead\",\n\t\t\treport.Fixes(edit.Fix(fmt.Sprintf(\"Use %s instead of %s\", alt, name), edit.ReplaceWithString(call.Fun, alt))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1006/sa1006_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1006\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1006/testdata/go1.0/CheckUnsafePrintf/CheckUnsafePrintf.go",
    "content": "package pkg\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc fn(s string) {\n\tfn2 := func() string { return \"\" }\n\tfmt.Printf(fn2())      //@ diag(`should use print-style function`)\n\t_ = fmt.Sprintf(fn2()) //@ diag(`should use print-style function`)\n\tlog.Printf(fn2())      //@ diag(`should use print-style function`)\n\tfmt.Printf(s)          //@ diag(`should use print-style function`)\n\tfmt.Printf(s, \"\")\n\tfmt.Fprintf(os.Stdout, s) //@ diag(`should use print-style function`)\n\tfmt.Fprintf(os.Stdout, s, \"\")\n\n\tfmt.Printf(fn2(), \"\")\n\tfmt.Printf(\"\")\n\tfmt.Printf(\"%s\", \"\")\n\tfmt.Printf(fn3())\n\n\tl := log.New(os.Stdout, \"\", 0)\n\tl.Printf(\"xx: %q\", \"yy\")\n\tl.Printf(s) //@ diag(`should use print-style function`)\n\n\tvar t testing.T\n\tt.Logf(fn2()) //@ diag(`should use print-style function`)\n\tt.Errorf(s)   //@ diag(`should use print-style function`)\n\tt.Fatalf(s)   //@ diag(`should use print-style function`)\n\tt.Skipf(s)    //@ diag(`should use print-style function`)\n\n\tvar b testing.B\n\tb.Logf(fn2()) //@ diag(`should use print-style function`)\n\tb.Errorf(s)   //@ diag(`should use print-style function`)\n\tb.Fatalf(s)   //@ diag(`should use print-style function`)\n\tb.Skipf(s)    //@ diag(`should use print-style function`)\n\n\tvar tb testing.TB\n\ttb.Logf(fn2()) //@ diag(`should use print-style function`)\n\ttb.Errorf(s)   //@ diag(`should use print-style function`)\n\ttb.Fatalf(s)   //@ diag(`should use print-style function`)\n\ttb.Skipf(s)    //@ diag(`should use print-style function`)\n\n\tfmt.Errorf(s) //@ diag(`should use print-style function`)\n\n\tvar nested struct {\n\t\tl log.Logger\n\t}\n\tnested.l.Printf(s) //@ diag(`should use print-style function`)\n}\n\nfunc fn3() (string, int) { return \"\", 0 }\n"
  },
  {
    "path": "staticcheck/sa1006/testdata/go1.0/CheckUnsafePrintf/CheckUnsafePrintf.go.golden",
    "content": "package pkg\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc fn(s string) {\n\tfn2 := func() string { return \"\" }\n\tfmt.Print(fn2())      //@ diag(`should use print-style function`)\n\t_ = fmt.Sprint(fn2()) //@ diag(`should use print-style function`)\n\tlog.Print(fn2())      //@ diag(`should use print-style function`)\n\tfmt.Print(s)          //@ diag(`should use print-style function`)\n\tfmt.Printf(s, \"\")\n\tfmt.Fprint(os.Stdout, s) //@ diag(`should use print-style function`)\n\tfmt.Fprintf(os.Stdout, s, \"\")\n\n\tfmt.Printf(fn2(), \"\")\n\tfmt.Printf(\"\")\n\tfmt.Printf(\"%s\", \"\")\n\tfmt.Printf(fn3())\n\n\tl := log.New(os.Stdout, \"\", 0)\n\tl.Printf(\"xx: %q\", \"yy\")\n\tl.Print(s) //@ diag(`should use print-style function`)\n\n\tvar t testing.T\n\tt.Log(fn2()) //@ diag(`should use print-style function`)\n\tt.Error(s)   //@ diag(`should use print-style function`)\n\tt.Fatal(s)   //@ diag(`should use print-style function`)\n\tt.Skip(s)    //@ diag(`should use print-style function`)\n\n\tvar b testing.B\n\tb.Log(fn2()) //@ diag(`should use print-style function`)\n\tb.Error(s)   //@ diag(`should use print-style function`)\n\tb.Fatal(s)   //@ diag(`should use print-style function`)\n\tb.Skip(s)    //@ diag(`should use print-style function`)\n\n\tvar tb testing.TB\n\ttb.Log(fn2()) //@ diag(`should use print-style function`)\n\ttb.Error(s)   //@ diag(`should use print-style function`)\n\ttb.Fatal(s)   //@ diag(`should use print-style function`)\n\ttb.Skip(s)    //@ diag(`should use print-style function`)\n\n\terrors.New(s) //@ diag(`should use print-style function`)\n\n\tvar nested struct {\n\t\tl log.Logger\n\t}\n\tnested.l.Print(s) //@ diag(`should use print-style function`)\n}\n\nfunc fn3() (string, int) { return \"\", 0 }\n"
  },
  {
    "path": "staticcheck/sa1007/sa1007.go",
    "content": "package sa1007\n\nimport (\n\t\"fmt\"\n\t\"go/constant\"\n\t\"net/url\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1007\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Invalid URL in \\'net/url.Parse\\'`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"net/url.Parse\": func(call *callcheck.Call) {\n\t\targ := call.Args[knowledge.Arg(\"net/url.Parse.rawurl\")]\n\t\tif c := callcheck.ExtractConstExpectKind(arg.Value, constant.String); c != nil {\n\t\t\ts := constant.StringVal(c.Value)\n\t\t\t_, err := url.Parse(s)\n\t\t\tif err != nil {\n\t\t\t\targ.Invalid(fmt.Sprintf(\"%q is not a valid URL: %s\", s, err))\n\t\t\t}\n\t\t}\n\t},\n}\n"
  },
  {
    "path": "staticcheck/sa1007/sa1007_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1007\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1007/testdata/go1.0/CheckURLs/CheckURLs.go",
    "content": "package pkg\n\nimport \"net/url\"\n\nfunc fn() {\n\turl.Parse(\"foobar\")\n\turl.Parse(\":\") //@ diag(`is not a valid URL`)\n\turl.Parse(\"https://golang.org\")\n}\n"
  },
  {
    "path": "staticcheck/sa1008/sa1008.go",
    "content": "package sa1008\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"net/http\"\n\t\"strconv\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1008\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Non-canonical key in \\'http.Header\\' map`,\n\t\tText: `Keys in \\'http.Header\\' maps are canonical, meaning they follow a specific\ncombination of uppercase and lowercase letters. Methods such as\n\\'http.Header.Add\\' and \\'http.Header.Del\\' convert inputs into this canonical\nform before manipulating the map.\n\nWhen manipulating \\'http.Header\\' maps directly, as opposed to using the\nprovided methods, care should be taken to stick to canonical form in\norder to avoid inconsistencies. The following piece of code\ndemonstrates one such inconsistency:\n\n    h := http.Header{}\n    h[\"etag\"] = []string{\"1234\"}\n    h.Add(\"etag\", \"5678\")\n    fmt.Println(h)\n\n    // Output:\n    // map[Etag:[5678] etag:[1234]]\n\nThe easiest way of obtaining the canonical form of a key is to use\n\\'http.CanonicalHeaderKey\\'.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node, push bool) bool {\n\t\tif !push {\n\t\t\treturn false\n\t\t}\n\t\tif assign, ok := node.(*ast.AssignStmt); ok {\n\t\t\t// TODO(dh): This risks missing some Header reads, for\n\t\t\t// example in `h1[\"foo\"] = h2[\"foo\"]` – these edge\n\t\t\t// cases are probably rare enough to ignore for now.\n\t\t\tfor _, expr := range assign.Lhs {\n\t\t\t\top, ok := expr.(*ast.IndexExpr)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif code.IsOfTypeWithName(pass, op.X, \"net/http.Header\") {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t\top, ok := node.(*ast.IndexExpr)\n\t\tif !ok {\n\t\t\treturn true\n\t\t}\n\t\tif !code.IsOfTypeWithName(pass, op.X, \"net/http.Header\") {\n\t\t\treturn true\n\t\t}\n\t\ts, ok := code.ExprToString(pass, op.Index)\n\t\tif !ok {\n\t\t\treturn true\n\t\t}\n\t\tcanonical := http.CanonicalHeaderKey(s)\n\t\tif s == canonical {\n\t\t\treturn true\n\t\t}\n\t\tvar fix analysis.SuggestedFix\n\t\tswitch op.Index.(type) {\n\t\tcase *ast.BasicLit:\n\t\t\tfix = edit.Fix(\"Canonicalize header key\", edit.ReplaceWithString(op.Index, strconv.Quote(canonical)))\n\t\tcase *ast.Ident:\n\t\t\tcall := &ast.CallExpr{\n\t\t\t\tFun:  edit.Selector(\"http\", \"CanonicalHeaderKey\"),\n\t\t\t\tArgs: []ast.Expr{op.Index},\n\t\t\t}\n\t\t\tfix = edit.Fix(\"Wrap in http.CanonicalHeaderKey\", edit.ReplaceWithNode(pass.Fset, op.Index, call))\n\t\t}\n\t\tmsg := fmt.Sprintf(\"keys in http.Header are canonicalized, %q is not canonical; fix the constant or use http.CanonicalHeaderKey\", s)\n\t\tif fix.Message != \"\" {\n\t\t\treport.Report(pass, op, msg, report.Fixes(fix))\n\t\t} else {\n\t\t\treport.Report(pass, op, msg)\n\t\t}\n\t\treturn true\n\t}\n\tpass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.AssignStmt)(nil), (*ast.IndexExpr)(nil)}, fn)\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1008/sa1008_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1008\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1008/testdata/go1.0/CheckCanonicalHeaderKey/CheckCanonicalHeaderKey.go",
    "content": "package pkg\n\nimport \"net/http\"\n\nfunc fn() {\n\tconst hdr = \"foo\"\n\tvar r http.Request\n\th := http.Header{}\n\tvar m map[string][]string\n\t_ = h[\"foo\"] //@ diag(`keys in http.Header are canonicalized`)\n\t_ = h[hdr]   //@ diag(`keys in http.Header are canonicalized`)\n\th[\"foo\"] = nil\n\t_ = r.Header[\"foo\"] //@ diag(`keys in http.Header are canonicalized`)\n\tr.Header[\"foo\"] = nil\n\t_ = m[\"foo\"]\n}\n"
  },
  {
    "path": "staticcheck/sa1008/testdata/go1.0/CheckCanonicalHeaderKey/CheckCanonicalHeaderKey.go.golden",
    "content": "package pkg\n\nimport \"net/http\"\n\nfunc fn() {\n\tconst hdr = \"foo\"\n\tvar r http.Request\n\th := http.Header{}\n\tvar m map[string][]string\n\t_ = h[\"Foo\"]                        //@ diag(`keys in http.Header are canonicalized`)\n\t_ = h[http.CanonicalHeaderKey(hdr)] //@ diag(`keys in http.Header are canonicalized`)\n\th[\"foo\"] = nil\n\t_ = r.Header[\"Foo\"] //@ diag(`keys in http.Header are canonicalized`)\n\tr.Header[\"foo\"] = nil\n\t_ = m[\"foo\"]\n}\n"
  },
  {
    "path": "staticcheck/sa1010/sa1010.go",
    "content": "package sa1010\n\nimport (\n\t\"fmt\"\n\t\"go/constant\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1010\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(checkRegexpFindAllRules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `\\'(*regexp.Regexp).FindAll\\' called with \\'n == 0\\', which will always return zero results`,\n\t\tText: `If \\'n >= 0\\', the function returns at most \\'n\\' matches/submatches. To\nreturn all results, specify a negative number.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkRegexpFindAllRules = map[string]callcheck.Check{\n\t\"(*regexp.Regexp).FindAll\":                    RepeatZeroTimes(\"a FindAll method\", 1),\n\t\"(*regexp.Regexp).FindAllIndex\":               RepeatZeroTimes(\"a FindAll method\", 1),\n\t\"(*regexp.Regexp).FindAllString\":              RepeatZeroTimes(\"a FindAll method\", 1),\n\t\"(*regexp.Regexp).FindAllStringIndex\":         RepeatZeroTimes(\"a FindAll method\", 1),\n\t\"(*regexp.Regexp).FindAllStringSubmatch\":      RepeatZeroTimes(\"a FindAll method\", 1),\n\t\"(*regexp.Regexp).FindAllStringSubmatchIndex\": RepeatZeroTimes(\"a FindAll method\", 1),\n\t\"(*regexp.Regexp).FindAllSubmatch\":            RepeatZeroTimes(\"a FindAll method\", 1),\n\t\"(*regexp.Regexp).FindAllSubmatchIndex\":       RepeatZeroTimes(\"a FindAll method\", 1),\n}\n\nfunc RepeatZeroTimes(name string, arg int) callcheck.Check {\n\treturn func(call *callcheck.Call) {\n\t\targ := call.Args[arg]\n\t\tif k, ok := arg.Value.Value.(*ir.Const); ok && k.Value.Kind() == constant.Int {\n\t\t\tif v, ok := constant.Int64Val(k.Value); ok && v == 0 {\n\t\t\t\targ.Invalid(fmt.Sprintf(\"calling %s with n == 0 will return no results, did you mean -1?\", name))\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1010/sa1010_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1010\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1010/testdata/go1.0/checkStdlibUsageRegexpFindAll/checkStdlibUsageRegexpFindAll.go",
    "content": "package pkg\n\nimport \"regexp\"\n\nfunc fn() {\n\tvar r *regexp.Regexp\n\t_ = r.FindAll(nil, 0) //@ diag(`calling a FindAll method with n == 0 will return no results`)\n}\n\nfunc fn2() {\n\tregexp.MustCompile(\"foo(\").FindAll(nil, 0) //@ diag(`calling a FindAll`)\n}\n"
  },
  {
    "path": "staticcheck/sa1011/sa1011.go",
    "content": "package sa1011\n\nimport (\n\t\"go/constant\"\n\t\"unicode/utf8\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1011\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(checkUTF8CutsetRules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Various methods in the \\\"strings\\\" package expect valid UTF-8, but invalid input is provided`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkUTF8CutsetRules = map[string]callcheck.Check{\n\t\"strings.IndexAny\":     check,\n\t\"strings.LastIndexAny\": check,\n\t\"strings.ContainsAny\":  check,\n\t\"strings.Trim\":         check,\n\t\"strings.TrimLeft\":     check,\n\t\"strings.TrimRight\":    check,\n}\n\nfunc check(call *callcheck.Call) {\n\targ := call.Args[1]\n\tif c := callcheck.ExtractConstExpectKind(arg.Value, constant.String); c != nil {\n\t\ts := constant.StringVal(c.Value)\n\t\tif !utf8.ValidString(s) {\n\t\t\targ.Invalid(\"argument is not a valid UTF-8 encoded string\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1011/sa1011_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1011\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1011/testdata/go1.0/checkStdlibUsageUTF8Cutset/checkStdlibUsageUTF8Cutset.go",
    "content": "package pkg\n\nimport \"strings\"\n\nfunc fn() {\n\tprintln(strings.Trim(\"\\x80test\\xff\", \"\\xff\")) //@ diag(`is not a valid UTF-8 encoded string`)\n\tprintln(strings.Trim(\"foo\", \"bar\"))\n\n\ts := \"\\xff\"\n\tif true {\n\t\ts = \"\"\n\t}\n\tprintln(strings.Trim(\"\", s))\n}\n"
  },
  {
    "path": "staticcheck/sa1012/sa1012.go",
    "content": "package sa1012\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1012\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `A nil \\'context.Context\\' is being passed to a function, consider using \\'context.TODO\\' instead`,\n\t\tText: `The context package prohibits the use of a \\'nil\\' context.\nIf no parent context is available, a new context should be used,\ne.g. \\'context.TODO\\' or \\'context.Background\\'.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkNilContextQ = pattern.MustParse(`(CallExpr fun@(Symbol _) (Builtin \"nil\"):_)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\ttodo := &ast.CallExpr{\n\t\tFun: edit.Selector(\"context\", \"TODO\"),\n\t}\n\tbg := &ast.CallExpr{\n\t\tFun: edit.Selector(\"context\", \"Background\"),\n\t}\n\tfor node, m := range code.Matches(pass, checkNilContextQ) {\n\t\tcall := node.(*ast.CallExpr)\n\t\tfun, ok := m.State[\"fun\"].(*types.Func)\n\t\tif !ok {\n\t\t\t// it might also be a builtin\n\t\t\tcontinue\n\t\t}\n\t\tsig := fun.Type().(*types.Signature)\n\t\tif sig.Params().Len() == 0 {\n\t\t\t// Our CallExpr might've matched a method expression, like\n\t\t\t// (*T).Foo(nil) – here, nil isn't the first argument of\n\t\t\t// the Foo method, but the method receiver.\n\t\t\tcontinue\n\t\t}\n\t\tif !typeutil.IsTypeWithName(sig.Params().At(0).Type(), \"context.Context\") {\n\t\t\tcontinue\n\t\t}\n\t\treport.Report(pass, call.Args[0],\n\t\t\t\"do not pass a nil Context, even if a function permits it; pass context.TODO if you are unsure about which Context to use\", report.Fixes(\n\t\t\t\tedit.Fix(\"Use context.TODO\", edit.ReplaceWithNode(pass.Fset, call.Args[0], todo)),\n\t\t\t\tedit.Fix(\"Use context.Background\", edit.ReplaceWithNode(pass.Fset, call.Args[0], bg))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1012/sa1012_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1012\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1012/testdata/go1.0/checkStdlibUsageNilContext/checkStdlibUsageNilContext.go",
    "content": "package pkg\n\nimport \"context\"\n\nfunc fn1(ctx context.Context)           {}\nfunc fn2(x string, ctx context.Context) {}\nfunc fn4()                              {}\n\ntype T struct{}\n\nfunc (*T) Foo() {}\n\nfunc fn3() {\n\tfn1(nil) //@ diag(`do not pass a nil Context`)\n\tfn1(context.TODO())\n\tfn2(\"\", nil)\n\tfn4()\n\n\t// don't flag this conversion\n\t_ = (func(context.Context))(nil)\n\t// and don't crash on these\n\t_ = (func())(nil)\n\t(*T).Foo(nil)\n}\n"
  },
  {
    "path": "staticcheck/sa1012/testdata/go1.0/checkStdlibUsageNilContext/checkStdlibUsageNilContext.go.golden",
    "content": "-- Use context.Background --\npackage pkg\n\nimport \"context\"\n\nfunc fn1(ctx context.Context)           {}\nfunc fn2(x string, ctx context.Context) {}\nfunc fn4()                              {}\n\ntype T struct{}\n\nfunc (*T) Foo() {}\n\nfunc fn3() {\n\tfn1(context.Background()) //@ diag(`do not pass a nil Context`)\n\tfn1(context.TODO())\n\tfn2(\"\", nil)\n\tfn4()\n\n\t// don't flag this conversion\n\t_ = (func(context.Context))(nil)\n\t// and don't crash on these\n\t_ = (func())(nil)\n\t(*T).Foo(nil)\n}\n-- Use context.TODO --\npackage pkg\n\nimport \"context\"\n\nfunc fn1(ctx context.Context)           {}\nfunc fn2(x string, ctx context.Context) {}\nfunc fn4()                              {}\n\ntype T struct{}\n\nfunc (*T) Foo() {}\n\nfunc fn3() {\n\tfn1(context.TODO()) //@ diag(`do not pass a nil Context`)\n\tfn1(context.TODO())\n\tfn2(\"\", nil)\n\tfn4()\n\n\t// don't flag this conversion\n\t_ = (func(context.Context))(nil)\n\t// and don't crash on these\n\t_ = (func())(nil)\n\t(*T).Foo(nil)\n}\n"
  },
  {
    "path": "staticcheck/sa1012/testdata/go1.18/checkStdlibUsageNilContext/checkStdlibUsageNilContext_generics.go",
    "content": "package pkg\n\nimport \"context\"\n\nfunc tpfn1[T any](ctx context.Context, x T)             {}\nfunc tpfn2[T1, T2 any](ctx context.Context, x T1, y T2) {}\n\nfunc tpbar() {\n\ttpfn1[int](nil, 0) //@ diag(`do not pass a nil Context`)\n\ttpfn1(nil, 0)      //@ diag(`do not pass a nil Context`)\n\n\ttpfn2[int, int](nil, 0, 0) //@ diag(`do not pass a nil Context`)\n\ttpfn2(nil, 0, 0)           //@ diag(`do not pass a nil Context`)\n}\n"
  },
  {
    "path": "staticcheck/sa1012/testdata/go1.18/checkStdlibUsageNilContext/checkStdlibUsageNilContext_generics.go.golden",
    "content": "-- Use context.Background --\npackage pkg\n\nimport \"context\"\n\nfunc tpfn1[T any](ctx context.Context, x T)             {}\nfunc tpfn2[T1, T2 any](ctx context.Context, x T1, y T2) {}\n\nfunc tpbar() {\n\ttpfn1[int](context.Background(), 0) //@ diag(`do not pass a nil Context`)\n\ttpfn1(context.Background(), 0)      //@ diag(`do not pass a nil Context`)\n\n\ttpfn2[int, int](context.Background(), 0, 0) //@ diag(`do not pass a nil Context`)\n\ttpfn2(context.Background(), 0, 0)           //@ diag(`do not pass a nil Context`)\n}\n-- Use context.TODO --\npackage pkg\n\nimport \"context\"\n\nfunc tpfn1[T any](ctx context.Context, x T)             {}\nfunc tpfn2[T1, T2 any](ctx context.Context, x T1, y T2) {}\n\nfunc tpbar() {\n\ttpfn1[int](context.TODO(), 0) //@ diag(`do not pass a nil Context`)\n\ttpfn1(context.TODO(), 0)      //@ diag(`do not pass a nil Context`)\n\n\ttpfn2[int, int](context.TODO(), 0, 0) //@ diag(`do not pass a nil Context`)\n\ttpfn2(context.TODO(), 0, 0)           //@ diag(`do not pass a nil Context`)\n}\n"
  },
  {
    "path": "staticcheck/sa1013/sa1013.go",
    "content": "package sa1013\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/knowledge\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1013\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `\\'io.Seeker.Seek\\' is being called with the whence constant as the first argument, but it should be the second`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckSeekerQ = pattern.MustParse(`(CallExpr fun@(SelectorExpr _ (Ident \"Seek\")) [arg1@(SelectorExpr _ (Symbol (Or \"io.SeekStart\" \"io.SeekCurrent\" \"io.SeekEnd\"))) arg2])`)\n\tcheckSeekerR = pattern.MustParse(`(CallExpr fun [arg2 arg1])`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkSeekerQ) {\n\t\tif !code.IsMethod(pass, m.State[\"fun\"].(*ast.SelectorExpr), \"Seek\", knowledge.Signatures[\"(io.Seeker).Seek\"]) {\n\t\t\tcontinue\n\t\t}\n\t\tedits := code.EditMatch(pass, node, m, checkSeekerR)\n\t\treport.Report(pass, node, \"the first argument of io.Seeker is the offset, but an io.Seek* constant is being used instead\",\n\t\t\treport.Fixes(edit.Fix(\"Swap arguments\", edits...)))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1013/sa1013_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1013\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1013/testdata/go1.0/checkStdlibUsageSeeker/checkStdlibUsageSeeker.go",
    "content": "package pkg\n\nimport (\n\t\"io\"\n\tmyio \"io\"\n\t\"os\"\n)\n\ntype T struct{}\n\nfunc (T) Seek(whence int, offset int64) (int64, error) {\n\t// This method does NOT implement io.Seeker\n\treturn 0, nil\n}\n\nfunc fn() {\n\tconst SeekStart = 0\n\tvar s io.Seeker\n\ts.Seek(0, 0)\n\ts.Seek(0, io.SeekStart)\n\ts.Seek(io.SeekStart, 0)   //@ diag(`the first argument of io.Seeker is the offset`)\n\ts.Seek(myio.SeekStart, 0) //@ diag(`the first argument of io.Seeker is the offset`)\n\ts.Seek(SeekStart, 0)\n\n\tvar f *os.File\n\tf.Seek(io.SeekStart, 0) //@ diag(`the first argument of io.Seeker is the offset`)\n\n\tvar t T\n\tt.Seek(io.SeekStart, 0) // not flagged, T is not an io.Seeker\n}\n"
  },
  {
    "path": "staticcheck/sa1013/testdata/go1.0/checkStdlibUsageSeeker/checkStdlibUsageSeeker.go.golden",
    "content": "package pkg\n\nimport (\n\t\"io\"\n\tmyio \"io\"\n\t\"os\"\n)\n\ntype T struct{}\n\nfunc (T) Seek(whence int, offset int64) (int64, error) {\n\t// This method does NOT implement io.Seeker\n\treturn 0, nil\n}\n\nfunc fn() {\n\tconst SeekStart = 0\n\tvar s io.Seeker\n\ts.Seek(0, 0)\n\ts.Seek(0, io.SeekStart)\n\ts.Seek(0, io.SeekStart)   //@ diag(`the first argument of io.Seeker is the offset`)\n\ts.Seek(0, myio.SeekStart) //@ diag(`the first argument of io.Seeker is the offset`)\n\ts.Seek(SeekStart, 0)\n\n\tvar f *os.File\n\tf.Seek(0, io.SeekStart) //@ diag(`the first argument of io.Seeker is the offset`)\n\n\tvar t T\n\tt.Seek(io.SeekStart, 0) // not flagged, T is not an io.Seeker\n}\n"
  },
  {
    "path": "staticcheck/sa1014/sa1014.go",
    "content": "package sa1014\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1014\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(checkUnmarshalPointerRules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Non-pointer value passed to \\'Unmarshal\\' or \\'Decode\\'`,\n\t\tText: `Functions such as \\'encoding/json.Unmarshal\\' and\n\\'(*encoding/json.Decoder).Decode\\' require a pointer to the value that should\nbe populated. Passing a non-pointer value results in the function returning an\nerror at runtime, as it cannot modify the target value.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkUnmarshalPointerRules = map[string]callcheck.Check{\n\t\"encoding/xml.Unmarshal\":                unmarshalPointer(\"xml.Unmarshal\", 1),\n\t\"(*encoding/xml.Decoder).Decode\":        unmarshalPointer(\"Decode\", 0),\n\t\"(*encoding/xml.Decoder).DecodeElement\": unmarshalPointer(\"DecodeElement\", 0),\n\t\"encoding/json.Unmarshal\":               unmarshalPointer(\"json.Unmarshal\", 1),\n\t\"(*encoding/json.Decoder).Decode\":       unmarshalPointer(\"Decode\", 0),\n}\n\nfunc unmarshalPointer(name string, arg int) callcheck.Check {\n\treturn func(call *callcheck.Call) {\n\t\tif !Pointer(call.Args[arg].Value) {\n\t\t\tcall.Args[arg].Invalid(fmt.Sprintf(\"%s expects to unmarshal into a pointer, but the provided value is not a pointer\", name))\n\t\t}\n\t}\n}\n\nfunc Pointer(v callcheck.Value) bool {\n\tswitch v.Value.Type().Underlying().(type) {\n\tcase *types.Pointer, *types.Interface:\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "staticcheck/sa1014/sa1014_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1014\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1014/testdata/go1.0/CheckUnmarshalPointer/CheckUnmarshalPointer.go",
    "content": "package pkg\n\nimport \"encoding/json\"\n\nfunc fn1(i3 interface{}) {\n\tvar v map[string]interface{}\n\tvar i1 interface{} = v\n\tvar i2 interface{} = &v\n\tp := &v\n\tjson.Unmarshal([]byte(`{}`), v) //@ diag(`Unmarshal expects to unmarshal into a pointer`)\n\tjson.Unmarshal([]byte(`{}`), &v)\n\tjson.Unmarshal([]byte(`{}`), i1) //@ diag(`Unmarshal expects to unmarshal into a pointer`)\n\tjson.Unmarshal([]byte(`{}`), i2)\n\tjson.Unmarshal([]byte(`{}`), i3)\n\tjson.Unmarshal([]byte(`{}`), p)\n\n\tjson.NewDecoder(nil).Decode(v) //@ diag(`Decode expects to unmarshal into a pointer`)\n}\n"
  },
  {
    "path": "staticcheck/sa1015/sa1015.go",
    "content": "package sa1015\n\nimport (\n\t\"go/token\"\n\t\"go/version\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1015\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Using \\'time.Tick\\' in a way that will leak. Consider using \\'time.NewTicker\\', and only use \\'time.Tick\\' in tests, commands and endless functions`,\n\n\t\tText: `Before Go 1.23, \\'time.Ticker\\'s had to be closed to be able to be garbage\ncollected. Since \\'time.Tick\\' doesn't make it possible to close the underlying\nticker, using it repeatedly would leak memory.\n\nGo 1.23 fixes this by allowing tickers to be collected even if they weren't closed.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tif fn.Pos() == token.NoPos || version.Compare(code.StdlibVersion(pass, fn), \"go1.23\") >= 0 {\n\t\t\t// Beginning with Go 1.23, the GC is able to collect unreferenced, unclosed\n\t\t\t// tickers, which makes time.Tick safe(r) to use.\n\t\t\t//\n\t\t\t// When we don't have a valid position, we err on the side of false negatives.\n\t\t\t// This shouldn't actually lead to any false negatives, as no functions\n\t\t\t// without valid positions (such as the synthesized init function) should be\n\t\t\t// able to use time.Tick.\n\t\t\tcontinue\n\t\t}\n\n\t\tif code.IsMainLike(pass) || code.IsInTest(pass, fn) {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, block := range fn.Blocks {\n\t\t\tfor _, ins := range block.Instrs {\n\t\t\t\tcall, ok := ins.(*ir.Call)\n\t\t\t\tif !ok || !irutil.IsCallTo(call.Common(), \"time.Tick\") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !irutil.Terminates(call.Parent()) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treport.Report(pass, call, \"using time.Tick leaks the underlying ticker, consider using it only in endless functions, tests and the main package, and use time.NewTicker here\")\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1015/sa1015_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1015\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1015/testdata/go1.0/CheckLeakyTimeTick/CheckLeakyTimeTick.go",
    "content": "package pkg\n\nimport \"time\"\n\nfunc fn1() {\n\tfor range time.Tick(0) {\n\t\tprintln(\"\")\n\t}\n}\n\nfunc fn2() {\n\tfor range time.Tick(0) { //@ diag(`leaks the underlying ticker`)\n\t\tprintln(\"\")\n\t\tif true {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc fn3() {\n\tfor range time.Tick(0) { //@ diag(`leaks the underlying ticker`)\n\t\tprintln(\"\")\n\t\tif true {\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc fn4() {\n\tgo func() {\n\t\tfor range time.Tick(0) {\n\t\t\tprintln(\"\")\n\t\t}\n\t}()\n}\n\nfunc fn5() {\n\tif false {\n\t\tpanic(\"foo\")\n\t}\n\tfor range time.Tick(0) {\n\t\tprintln(\"\")\n\t}\n}\n\n\ntype T struct{}\n\nfunc (t *T) foo() {\n\tfor range time.Tick(0) {\n\t\tprintln(\"\")\n\t}\n}\n\nfunc (t T) bar() {\n\tfor range time.Tick(0) {\n\t\tprintln(\"\")\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1015/testdata/go1.0/CheckLeakyTimeTick-main/CheckLeakyTimeTick-main.go",
    "content": "package main\n\nimport \"time\"\n\nfunc fn2() {\n\tfor range time.Tick(0) {\n\t\tprintln(\"\")\n\t\tif true {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc main() {\n\t_ = time.Tick(0)\n}\n"
  },
  {
    "path": "staticcheck/sa1015/testdata/go1.23/CheckLeakyTimeTick/CheckLeakyTimeTick.go",
    "content": "package pkg\n\nimport \"time\"\n\nfunc fn1() {\n\t// Not flagged because this is no longer a problem in Go 1.23.\n\tfor range time.Tick(0) {\n\t\tprintln(\"\")\n\t\tif true {\n\t\t\tbreak\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1016/sa1016.go",
    "content": "package sa1016\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1016\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Trapping a signal that cannot be trapped`,\n\t\tText: `Not all signals can be intercepted by a process. Specifically, on\nUNIX-like systems, the \\'syscall.SIGKILL\\' and \\'syscall.SIGSTOP\\' signals are\nnever passed to the process, but instead handled directly by the\nkernel. It is therefore pointless to try and handle these signals.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar query = pattern.MustParse(`\n\t(CallExpr\n\t\t(Symbol\n\t\t\t(Or\n\t\t\t\t\"os/signal.Ignore\"\n\t\t\t\t\"os/signal.Notify\"\n\t\t\t\t\"os/signal.Reset\"))\n\t\t_)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tisSignal := func(pass *analysis.Pass, expr ast.Expr, name string) bool {\n\t\tif expr, ok := expr.(*ast.SelectorExpr); ok {\n\t\t\treturn code.SelectorName(pass, expr) == name\n\t\t} else {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tfor node := range code.Matches(pass, query) {\n\t\tcall := node.(*ast.CallExpr)\n\t\thasSigterm := false\n\t\tfor _, arg := range call.Args {\n\t\t\tif conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, \"os.Signal\") {\n\t\t\t\targ = conv.Args[0]\n\t\t\t}\n\n\t\t\tif isSignal(pass, arg, \"syscall.SIGTERM\") {\n\t\t\t\thasSigterm = true\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t}\n\t\tfor i, arg := range call.Args {\n\t\t\tif conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, \"os.Signal\") {\n\t\t\t\targ = conv.Args[0]\n\t\t\t}\n\n\t\t\tif isSignal(pass, arg, \"os.Kill\") || isSignal(pass, arg, \"syscall.SIGKILL\") {\n\t\t\t\tvar fixes []analysis.SuggestedFix\n\t\t\t\tif !hasSigterm {\n\t\t\t\t\tnargs := make([]ast.Expr, len(call.Args))\n\t\t\t\t\tfor j, a := range call.Args {\n\t\t\t\t\t\tif i == j {\n\t\t\t\t\t\t\tnargs[j] = edit.Selector(\"syscall\", \"SIGTERM\")\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnargs[j] = a\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tncall := *call\n\t\t\t\t\tncall.Args = nargs\n\t\t\t\t\tfixes = append(fixes, edit.Fix(fmt.Sprintf(\"Use syscall.SIGTERM instead of %s\", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall)))\n\t\t\t\t}\n\t\t\t\tnargs := make([]ast.Expr, 0, len(call.Args))\n\t\t\t\tfor j, a := range call.Args {\n\t\t\t\t\tif i == j {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tnargs = append(nargs, a)\n\t\t\t\t}\n\t\t\t\tncall := *call\n\t\t\t\tncall.Args = nargs\n\t\t\t\tfixes = append(fixes, edit.Fix(fmt.Sprintf(\"Remove %s from list of arguments\", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall)))\n\t\t\t\treport.Report(pass, arg, fmt.Sprintf(\"%s cannot be trapped (did you mean syscall.SIGTERM?)\", report.Render(pass, arg)), report.Fixes(fixes...))\n\t\t\t}\n\t\t\tif isSignal(pass, arg, \"syscall.SIGSTOP\") {\n\t\t\t\tnargs := make([]ast.Expr, 0, len(call.Args)-1)\n\t\t\t\tfor j, a := range call.Args {\n\t\t\t\t\tif i == j {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tnargs = append(nargs, a)\n\t\t\t\t}\n\t\t\t\tncall := *call\n\t\t\t\tncall.Args = nargs\n\t\t\t\treport.Report(pass, arg, \"syscall.SIGSTOP cannot be trapped\", report.Fixes(edit.Fix(\"Remove syscall.SIGSTOP from list of arguments\", edit.ReplaceWithNode(pass.Fset, call, &ncall))))\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1016/sa1016_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1016\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1016/testdata/go1.0/CheckUntrappableSignal/CheckUntrappableSignal.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc fn() {\n\tc := make(chan os.Signal, 1)\n\tsignal.Notify(c, os.Interrupt)\n\tsignal.Ignore(os.Signal(syscall.SIGKILL)) //@ diag(`cannot be trapped`)\n\tsignal.Ignore(os.Kill)                    //@ diag(`cannot be trapped`)\n\tsignal.Notify(c, os.Kill)                 //@ diag(`cannot be trapped`)\n\tsignal.Reset(os.Kill)                     //@ diag(`cannot be trapped`)\n\tsignal.Ignore(syscall.SIGKILL)            //@ diag(`cannot be trapped`)\n\tsignal.Notify(c, syscall.SIGKILL)         //@ diag(`cannot be trapped`)\n\tsignal.Reset(syscall.SIGKILL)             //@ diag(`cannot be trapped`)\n}\n"
  },
  {
    "path": "staticcheck/sa1016/testdata/go1.0/CheckUntrappableSignal/CheckUntrappableSignal.go.golden",
    "content": "-- Remove syscall.SIGKILL from list of arguments --\npackage main\n\nimport (\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc fn() {\n\tc := make(chan os.Signal, 1)\n\tsignal.Notify(c, os.Interrupt)\n\tsignal.Ignore()           //@ diag(`cannot be trapped`)\n\tsignal.Ignore(os.Kill)    //@ diag(`cannot be trapped`)\n\tsignal.Notify(c, os.Kill) //@ diag(`cannot be trapped`)\n\tsignal.Reset(os.Kill)     //@ diag(`cannot be trapped`)\n\tsignal.Ignore()           //@ diag(`cannot be trapped`)\n\tsignal.Notify(c)          //@ diag(`cannot be trapped`)\n\tsignal.Reset()            //@ diag(`cannot be trapped`)\n}\n\n-- Remove os.Kill from list of arguments --\npackage main\n\nimport (\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc fn() {\n\tc := make(chan os.Signal, 1)\n\tsignal.Notify(c, os.Interrupt)\n\tsignal.Ignore(os.Signal(syscall.SIGKILL)) //@ diag(`cannot be trapped`)\n\tsignal.Ignore()                           //@ diag(`cannot be trapped`)\n\tsignal.Notify(c)                          //@ diag(`cannot be trapped`)\n\tsignal.Reset()                            //@ diag(`cannot be trapped`)\n\tsignal.Ignore(syscall.SIGKILL)            //@ diag(`cannot be trapped`)\n\tsignal.Notify(c, syscall.SIGKILL)         //@ diag(`cannot be trapped`)\n\tsignal.Reset(syscall.SIGKILL)             //@ diag(`cannot be trapped`)\n}\n\n-- Use syscall.SIGTERM instead of syscall.SIGKILL --\npackage main\n\nimport (\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc fn() {\n\tc := make(chan os.Signal, 1)\n\tsignal.Notify(c, os.Interrupt)\n\tsignal.Ignore(syscall.SIGTERM)    //@ diag(`cannot be trapped`)\n\tsignal.Ignore(os.Kill)            //@ diag(`cannot be trapped`)\n\tsignal.Notify(c, os.Kill)         //@ diag(`cannot be trapped`)\n\tsignal.Reset(os.Kill)             //@ diag(`cannot be trapped`)\n\tsignal.Ignore(syscall.SIGTERM)    //@ diag(`cannot be trapped`)\n\tsignal.Notify(c, syscall.SIGTERM) //@ diag(`cannot be trapped`)\n\tsignal.Reset(syscall.SIGTERM)     //@ diag(`cannot be trapped`)\n}\n\n-- Use syscall.SIGTERM instead of os.Kill --\npackage main\n\nimport (\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc fn() {\n\tc := make(chan os.Signal, 1)\n\tsignal.Notify(c, os.Interrupt)\n\tsignal.Ignore(os.Signal(syscall.SIGKILL)) //@ diag(`cannot be trapped`)\n\tsignal.Ignore(syscall.SIGTERM)            //@ diag(`cannot be trapped`)\n\tsignal.Notify(c, syscall.SIGTERM)         //@ diag(`cannot be trapped`)\n\tsignal.Reset(syscall.SIGTERM)             //@ diag(`cannot be trapped`)\n\tsignal.Ignore(syscall.SIGKILL)            //@ diag(`cannot be trapped`)\n\tsignal.Notify(c, syscall.SIGKILL)         //@ diag(`cannot be trapped`)\n\tsignal.Reset(syscall.SIGKILL)             //@ diag(`cannot be trapped`)\n}\n"
  },
  {
    "path": "staticcheck/sa1016/testdata/go1.0/CheckUntrappableSignal/CheckUntrappableSignal_unix.go",
    "content": "//go:build android || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris\n// +build android darwin dragonfly freebsd linux netbsd openbsd solaris\n\npackage main\n\nimport (\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc fn2() {\n\tc := make(chan os.Signal, 1)\n\tsignal.Ignore(syscall.SIGSTOP)    //@ diag(`cannot be trapped`)\n\tsignal.Notify(c, syscall.SIGSTOP) //@ diag(`cannot be trapped`)\n\tsignal.Reset(syscall.SIGSTOP)     //@ diag(`cannot be trapped`)\n}\n"
  },
  {
    "path": "staticcheck/sa1016/testdata/go1.0/CheckUntrappableSignal/CheckUntrappableSignal_unix.go.golden",
    "content": "//go:build android || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris\n// +build android darwin dragonfly freebsd linux netbsd openbsd solaris\n\npackage main\n\nimport (\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc fn2() {\n\tc := make(chan os.Signal, 1)\n\tsignal.Ignore()  //@ diag(`cannot be trapped`)\n\tsignal.Notify(c) //@ diag(`cannot be trapped`)\n\tsignal.Reset()   //@ diag(`cannot be trapped`)\n}\n"
  },
  {
    "path": "staticcheck/sa1017/sa1017.go",
    "content": "package sa1017\n\nimport (\n\t\"go/constant\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1017\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Channels used with \\'os/signal.Notify\\' should be buffered`,\n\t\tText: `The \\'os/signal\\' package uses non-blocking channel sends when delivering\nsignals. If the receiving end of the channel isn't ready and the\nchannel is either unbuffered or full, the signal will be dropped. To\navoid missing signals, the channel should be buffered and of the\nappropriate size. For a channel used for notification of just one\nsignal value, a buffer of size 1 is sufficient.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"os/signal.Notify\": func(call *callcheck.Call) {\n\t\targ := call.Args[knowledge.Arg(\"os/signal.Notify.c\")]\n\t\tif isUnbufferedChannel(arg.Value) {\n\t\t\targ.Invalid(\"the channel used with signal.Notify should be buffered\")\n\t\t}\n\t},\n}\n\nfunc isUnbufferedChannel(v callcheck.Value) bool {\n\t// TODO(dh): this check of course misses many cases of unbuffered\n\t// channels, such as any in phi or sigma nodes. We'll eventually\n\t// replace this function.\n\tval := v.Value\n\tif ct, ok := val.(*ir.ChangeType); ok {\n\t\tval = ct.X\n\t}\n\tmk, ok := val.(*ir.MakeChan)\n\tif !ok {\n\t\treturn false\n\t}\n\tif k, ok := mk.Size.(*ir.Const); ok && k.Value.Kind() == constant.Int {\n\t\tif v, ok := constant.Int64Val(k.Value); ok && v == 0 {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "staticcheck/sa1017/sa1017_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1017\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1017/testdata/go1.0/CheckUnbufferedSignalChan/CheckUnbufferedSignalChan.go",
    "content": "package pkg\n\nimport (\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc fn(b bool) {\n\tc0 := make(chan os.Signal)\n\tsignal.Notify(c0, os.Interrupt) //@ diag(`the channel used with signal.Notify should be buffered`)\n\n\tc1 := make(chan os.Signal, 1)\n\tsignal.Notify(c1, os.Interrupt, syscall.SIGHUP)\n\n\tc2 := c0\n\tif b {\n\t\tc2 = c1\n\t}\n\tsignal.Notify(c2, os.Interrupt, syscall.SIGHUP)\n}\n"
  },
  {
    "path": "staticcheck/sa1018/sa1018.go",
    "content": "package sa1018\n\nimport (\n\t\"fmt\"\n\t\"go/constant\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1018\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `\\'strings.Replace\\' called with \\'n == 0\\', which does nothing`,\n\t\tText: `With \\'n == 0\\', zero instances will be replaced. To replace all\ninstances, use a negative number, or use \\'strings.ReplaceAll\\'.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"strings.Replace\": check(\"strings.Replace\", 3),\n\t\"bytes.Replace\":   check(\"bytes.Replace\", 3),\n}\n\nfunc check(name string, arg int) callcheck.Check {\n\treturn func(call *callcheck.Call) {\n\t\targ := call.Args[arg]\n\t\tif k, ok := arg.Value.Value.(*ir.Const); ok && k.Value.Kind() == constant.Int {\n\t\t\tif v, ok := constant.Int64Val(k.Value); ok && v == 0 {\n\t\t\t\targ.Invalid(fmt.Sprintf(\"calling %s with n == 0 will return no results, did you mean -1?\", name))\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1018/sa1018_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1018\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1018/testdata/go1.0/CheckStringsReplaceZero/CheckStringsReplaceZero.go",
    "content": "package pkg\n\nimport \"strings\"\n\nfunc fn() {\n\t_ = strings.Replace(\"\", \"\", \"\", 0) //@ diag(`calling strings.Replace with n == 0`)\n\t_ = strings.Replace(\"\", \"\", \"\", -1)\n\t_ = strings.Replace(\"\", \"\", \"\", 1)\n}\n"
  },
  {
    "path": "staticcheck/sa1019/sa1019.go",
    "content": "package sa1019\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\t\"go/version\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/deprecated\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1019\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, deprecated.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Using a deprecated function, variable, constant or field`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityDeprecated,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc formatGoVersion(s string) string {\n\treturn \"Go \" + strings.TrimPrefix(s, \"go\")\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tdeprs := pass.ResultOf[deprecated.Analyzer].(deprecated.Result)\n\n\t// Selectors can appear outside of function literals, e.g. when\n\t// declaring package level variables.\n\n\tisStdlibPath := func(path string) bool {\n\t\t// Modules with no dot in the first path element are reserved for the standard library and tooling.\n\t\t// This is the best we can currently do.\n\t\t// Nobody tells us which import paths are part of the standard library.\n\t\t//\n\t\t// We check the entire path instead of just the first path element, because the standard library doesn't contain paths with any dots, anyway.\n\n\t\treturn !strings.Contains(path, \".\")\n\t}\n\n\thandleDeprecation := func(depr *deprecated.IsDeprecated, node ast.Node, deprecatedObjName string, pkgPath string, tfn types.Object) {\n\t\tstd, ok := knowledge.StdlibDeprecations[deprecatedObjName]\n\t\tif !ok && isStdlibPath(pkgPath) {\n\t\t\t// Deprecated object in the standard library, but we don't know the details of the deprecation.\n\t\t\t// Don't flag it at all, to avoid flagging an object that was deprecated in 1.N when targeting 1.N-1.\n\t\t\t// See https://staticcheck.dev/issues/1108 for the background on this.\n\t\t\treturn\n\t\t}\n\t\tif ok {\n\t\t\t// In the past, we made use of the AlternativeAvailableSince field. If a function was deprecated in Go\n\t\t\t// 1.6 and an alternative had been available in Go 1.0, then we'd recommend using the alternative even\n\t\t\t// if targeting Go 1.2. The idea was to suggest writing future-proof code by using already-existing\n\t\t\t// alternatives. This had a major flaw, however: the user would need to use at least Go 1.6 for\n\t\t\t// Staticcheck to know that the function had been deprecated. Thus, targeting Go 1.2 and using Go 1.2\n\t\t\t// would behave differently from targeting Go 1.2 and using Go 1.6. This is especially a problem if the\n\t\t\t// user tries to ignore the warning. Depending on the Go version in use, the ignore directive may or may\n\t\t\t// not match, causing a warning of its own.\n\t\t\t//\n\t\t\t// To avoid this issue, we no longer try to be smart. We now only compare the targeted version against\n\t\t\t// the version that deprecated an object.\n\t\t\t//\n\t\t\t// Unfortunately, this issue also applies to AlternativeAvailableSince == DeprecatedNeverUse. Even though it\n\t\t\t// is only applied to seriously flawed API, such as broken cryptography, users may wish to ignore those\n\t\t\t// warnings.\n\t\t\t//\n\t\t\t// See also https://staticcheck.dev/issues/1318.\n\t\t\tif version.Compare(code.StdlibVersion(pass, node), std.DeprecatedSince) == -1 {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif tfn != nil {\n\t\t\tif _, ok := deprs.Objects[tfn]; ok {\n\t\t\t\t// functions that are deprecated may use deprecated\n\t\t\t\t// symbols\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif ok {\n\t\t\tswitch std.AlternativeAvailableSince {\n\t\t\tcase knowledge.DeprecatedNeverUse:\n\t\t\t\treport.Report(pass, node,\n\t\t\t\t\tfmt.Sprintf(\"%s has been deprecated since %s because it shouldn't be used: %s\",\n\t\t\t\t\t\tdeprecatedObjName, formatGoVersion(std.DeprecatedSince), depr.Msg))\n\t\t\tcase std.DeprecatedSince, knowledge.DeprecatedUseNoLonger:\n\t\t\t\treport.Report(pass, node,\n\t\t\t\t\tfmt.Sprintf(\"%s has been deprecated since %s: %s\",\n\t\t\t\t\t\tdeprecatedObjName, formatGoVersion(std.DeprecatedSince), depr.Msg))\n\t\t\tdefault:\n\t\t\t\treport.Report(pass, node,\n\t\t\t\t\tfmt.Sprintf(\"%s has been deprecated since %s and an alternative has been available since %s: %s\",\n\t\t\t\t\t\tdeprecatedObjName, formatGoVersion(std.DeprecatedSince), formatGoVersion(std.AlternativeAvailableSince), depr.Msg))\n\t\t\t}\n\t\t} else {\n\t\t\treport.Report(pass, node, fmt.Sprintf(\"%s is deprecated: %s\", deprecatedObjName, depr.Msg))\n\t\t}\n\t}\n\n\tvar tfn types.Object\n\tstack := 0\n\n\tcheckIdentObj := func(sel *ast.SelectorExpr) bool {\n\t\tobj := pass.TypesInfo.ObjectOf(sel.Sel)\n\n\t\tif obj_, ok := obj.(*types.Func); ok {\n\t\t\tobj = obj_.Origin()\n\t\t}\n\t\tif obj.Pkg() == nil {\n\t\t\treturn true\n\t\t}\n\t\tif obj.Pkg() == pass.Pkg {\n\t\t\t// A package is allowed to use its own deprecated objects\n\t\t\treturn true\n\t\t}\n\n\t\t// A package \"foo\" has two related packages \"foo_test\" and \"foo.test\", for external tests and the package main\n\t\t// generated by 'go test' respectively. \"foo_test\" can import and use \"foo\", \"foo.test\" imports and uses \"foo\"\n\t\t// and \"foo_test\".\n\n\t\tif strings.TrimSuffix(pass.Pkg.Path(), \"_test\") == obj.Pkg().Path() {\n\t\t\t// foo_test (the external tests of foo) can use objects from foo.\n\t\t\treturn true\n\t\t}\n\t\tif strings.TrimSuffix(pass.Pkg.Path(), \".test\") == obj.Pkg().Path() {\n\t\t\t// foo.test (the main package of foo's tests) can use objects from foo.\n\t\t\treturn true\n\t\t}\n\t\tif strings.TrimSuffix(pass.Pkg.Path(), \".test\") == strings.TrimSuffix(obj.Pkg().Path(), \"_test\") {\n\t\t\t// foo.test (the main package of foo's tests) can use objects from foo's external tests.\n\t\t\treturn true\n\t\t}\n\n\t\tnode := ast.Node(sel)\n\t\tif pass.TypesInfo.Types[sel.X].IsType() {\n\t\t\tnode = sel.Sel\n\t\t}\n\t\tif depr, ok := deprs.Objects[obj]; ok {\n\t\t\thandleDeprecation(depr, node, code.SelectorName(pass, sel), obj.Pkg().Path(), tfn)\n\t\t}\n\t\treturn true\n\t}\n\n\tfn := func(node ast.Node, push bool) bool {\n\t\tif !push {\n\t\t\tstack--\n\t\t\treturn false\n\t\t}\n\t\tstack++\n\t\tif stack == 1 {\n\t\t\ttfn = nil\n\t\t}\n\t\tif fn, ok := node.(*ast.FuncDecl); ok {\n\t\t\ttfn = pass.TypesInfo.ObjectOf(fn.Name)\n\t\t}\n\n\t\tswitch v := node.(type) {\n\t\t// FIXME(dh): this misses dot-imported objects\n\t\tcase *ast.SelectorExpr:\n\t\t\treturn checkIdentObj(v)\n\n\t\tcase *ast.CompositeLit:\n\t\t\tlitType := pass.TypesInfo.Types[v.Type]\n\t\t\tif !litType.IsType() {\n\t\t\t\t// This is probably unreachable.\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif _, ok := litType.Type.Underlying().(*types.Struct); !ok {\n\t\t\t\t// We don't want to look at expressions in map initializers, for\n\t\t\t\t// example.\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tfor _, elt := range v.Elts {\n\t\t\t\tkv, ok := elt.(*ast.KeyValueExpr)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tkey, ok := kv.Key.(*ast.Ident)\n\t\t\t\tif !ok {\n\t\t\t\t\t// This is probably unreachable, since we're looking at keys\n\t\t\t\t\t// in a struct initializer.\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tsel := &ast.SelectorExpr{X: v.Type, Sel: key}\n\t\t\t\tcheckIdentObj(sel)\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\n\tfn2 := func(node ast.Node) {\n\t\tspec := node.(*ast.ImportSpec)\n\t\tvar imp *types.Package\n\t\tif spec.Name != nil {\n\t\t\timp = pass.TypesInfo.ObjectOf(spec.Name).(*types.PkgName).Imported()\n\t\t} else {\n\t\t\timp = pass.TypesInfo.Implicits[spec].(*types.PkgName).Imported()\n\t\t}\n\n\t\tp := spec.Path.Value\n\t\tpath := p[1 : len(p)-1]\n\t\tif depr, ok := deprs.Packages[imp]; ok {\n\t\t\tif path == \"github.com/golang/protobuf/proto\" {\n\t\t\t\tgen, ok := code.Generator(pass, spec.Path.Pos())\n\t\t\t\tif ok && gen == generated.ProtocGenGo {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif strings.TrimSuffix(pass.Pkg.Path(), \"_test\") == path {\n\t\t\t\t// foo_test can import foo\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif strings.TrimSuffix(pass.Pkg.Path(), \".test\") == path {\n\t\t\t\t// foo.test can import foo\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif strings.TrimSuffix(pass.Pkg.Path(), \".test\") == strings.TrimSuffix(path, \"_test\") {\n\t\t\t\t// foo.test can import foo_test\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\thandleDeprecation(depr, spec.Path, path, path, nil)\n\t\t}\n\t}\n\tpass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes(nil, fn)\n\tcode.Preorder(pass, fn2, (*ast.ImportSpec)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1019/sa1019_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1019\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.0/AnotherCheckDeprecated.assist/CheckDeprecatedassist.go",
    "content": "// Package pkg is a nice package.\n//\n// Deprecated: Alas, it is deprecated.\npackage AnotherCheckDeprecatedassist\n\nfunc Fn() {}\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.0/CheckDeprecated/CheckDeprecated.go",
    "content": "// Deprecated: this package is deprecated.\npackage pkg\n\nimport _ \"example.com/CheckDeprecated.assist\"          //@ diag(`Alas, it is deprecated.`)\nimport _ \"example.com/AnotherCheckDeprecated.assist\"   //@ diag(`Alas, it is deprecated.`)\nimport foo \"example.com/AnotherCheckDeprecated.assist\" //@ diag(`Alas, it is deprecated.`)\nimport \"example.com/AnotherCheckDeprecated.assist\"     //@ diag(`Alas, it is deprecated.`)\nimport ae \"example.com/CheckDeprecated.assist_external\"\n\nfunc init() {\n\tfoo.Fn()\n\tAnotherCheckDeprecatedassist.Fn()\n\n\t// Field is deprecated, but we're using it from the same package, which is fine.\n\tvar s S\n\t_ = s.Field\n\n\ts2 := ae.SD{\n\t\tD: \"used\", //@ diag(`external don't use me`)\n\t}\n\t_ = s2\n\t// Struct with the same name should not be flagged\n\ts3 := ae.SN{\n\t\tD:\"also\",\n\t}\n\t_ = s3\n\t// Other Key-Value expressions should be safely ignored\n\t_ = map[string]string {\n\t\t\"left\":\"right\",\n\t}\n}\n\n\ntype S struct {\n\t // Deprecated: this is deprecated.\n\t Field int\n}\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.0/CheckDeprecated/CheckDeprecated_test.go",
    "content": "package pkg\n\nimport \"testing\"\n\n// Deprecated: deprecating tests is silly but possible.\nfunc TestFoo(t *testing.T) {\n\tvar s S\n\t// Internal tests can use deprecated objects from the package they test.\n\t_ = s.Field\n}\n\n// This test isn't deprecated, to test that s.Field doesn't get flagged because it's from the package under test. If\n// TestBar was itself deprecated, it could use any deprecated objects it wanted.\nfunc TestBar(t *testing.T) {\n\tvar s S\n\t// Internal tests can use deprecated objects from the package under test\n\t_ = s.Field\n}\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.0/CheckDeprecated/external_test.go",
    "content": "// Deprecated: deprecating external test packages is silly but possible.\npackage pkg_test\n\nimport (\n\t\"testing\"\n\n\t// External tests can import deprecated packages under test.\n\tpkg \"example.com/CheckDeprecated\"\n)\n\n// Deprecated: deprecating tests is silly but possible.\nfunc TestFoo(t *testing.T) {\n}\n\n// This test isn't deprecated, to test that s.Field doesn't get flagged because it's from the package under test. If\n// TestBar was itself deprecated, it could use any deprecated objects it wanted.\nfunc TestBar(t *testing.T) {\n\tvar s pkg.S\n\t// External tests can use deprecated objects from the package under test\n\t_ = s.Field\n}\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.0/CheckDeprecated/not-protobuf.go",
    "content": "package pkg\n\nimport _ \"github.com/golang/protobuf/proto\" //@ diag(`Alas, it is deprecated.`)\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.0/CheckDeprecated/protobuf.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n\npackage pkg\n\nimport _ \"github.com/golang/protobuf/proto\"\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.0/CheckDeprecated.assist/CheckDeprecatedassist.go",
    "content": "// Package pkg is a nice package.\n//\n// Deprecated: Alas, it is deprecated.\npackage pkg\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.0/CheckDeprecated.assist_external/CheckDeprecatedassist_external.go",
    "content": "package pkg\n\ntype SD struct {\n\t// Deprecated: external don't use me\n\tD string\n}\n\ntype SN struct {\n\t// Not deprecated, but named the same\n\tD string\n}\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.0/vendor/github.com/golang/protobuf/proto/pkg.go",
    "content": "// Package proto exists.\n//\n// Deprecated: Alas, it is deprecated.\npackage proto\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.18/CheckDeprecated/CheckDeprecated_generics.go",
    "content": "package pkg\n\nimport pkg \"example.com/CheckDeprecated.assist\"\n\nfunc tpFn() {\n\tvar x pkg.S[int]\n\tx.Foo()\n\tx.Bar() //@ diag(`deprecated`)\n\tx.Baz() //@ diag(`deprecated`)\n\tx.Qux()\n\t_ = x.Field1\n\t_ = x.Field2 // This should be flagged, but see issue 1215\n}\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.18/CheckDeprecated.assist/CheckDeprecatedassist_generics.go",
    "content": "package pkg\n\ntype S[T any] struct {\n\tField1 T\n\t// Deprecated: don't use me\n\tField2 T\n}\n\nfunc (S[T]) Foo() {}\n\n// Deprecated: don't use me\nfunc (S[T]) Bar() {}\n\n// Deprecated: don't use me\nfunc (S[T]) Baz() {}\n\nfunc (S[T]) Qux() {}\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.19/CheckDeprecated/CheckDeprecated.go",
    "content": "package pkg\n\nimport _ \"io/ioutil\" //@ diag(\"has been deprecated\")\n\n// We test this in Go 1.19 even though io/ioutil has technically been deprecated since Go 1.16, because only in Go 1.19\n// was the proper deprecation marker added.\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.19/CheckDeprecated/stub.go",
    "content": "package pkg\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.3/CheckDeprecated/CheckDeprecated.go",
    "content": "package pkg\n\nimport (\n\t\"crypto/x509\"\n\t\"net/http/httputil\"\n\t\"path/filepath\"\n)\n\nfunc fn() {\n\tfilepath.HasPrefix(\"\", \"\") //@ diag(`filepath.HasPrefix has been deprecated since Go 1.0 because it shouldn't be used:`)\n\t_ = httputil.ErrPersistEOF //@ diag(`httputil.ErrPersistEOF has been deprecated since Go 1.0:`)\n\t_ = httputil.ServerConn{}  //@ diag(`httputil.ServerConn has been deprecated since Go 1.0:`)\n\t_ = x509.CertificateRequest{}.Attributes\n}\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.4/CheckDeprecated/CheckDeprecated.go",
    "content": "package pkg\n\nimport (\n\t\"compress/flate\"\n\t\"database/sql/driver\"\n\t\"net/http\"\n\t\"os\"\n\t\"syscall\"\n)\n\nvar _ = syscall.StringByteSlice(\"\") //@ diag(`Use ByteSliceFromString instead`)\n\nfunc fn1(err error) {\n\tvar r *http.Request\n\t_ = r.Cancel\n\t_ = syscall.StringByteSlice(\"\") //@ diag(`Use ByteSliceFromString instead`)\n\t_ = os.SEEK_SET\n\tvar _ flate.ReadError\n\n\tvar tr *http.Transport\n\ttr.CancelRequest(nil)\n\n\tvar conn driver.Conn\n\tconn.Begin()\n}\n\n// Deprecated: Don't use this.\nfunc fn2() {\n\t_ = syscall.StringByteSlice(\"\")\n\n\tanon := func(x int) {\n\t\tprintln(x)\n\t\t_ = syscall.StringByteSlice(\"\")\n\t}\n\tanon(1)\n}\n"
  },
  {
    "path": "staticcheck/sa1019/testdata/go1.8/CheckDeprecated/CheckDeprecated.go",
    "content": "package pkg\n\nimport (\n\t\"compress/flate\"\n\t\"crypto/x509\"\n\t\"database/sql/driver\"\n\t\"net/http\"\n\t\"os\"\n\t\"syscall\"\n)\n\nvar _ = syscall.StringByteSlice(\"\") //@ diag(`Use ByteSliceFromString instead`)\n\nfunc fn1(err error) {\n\tvar r http.Request\n\tvar rp *http.Request\n\t_ = r.Cancel                        //@ diag(re`deprecated since Go 1\\.7:.+If a Request's Cancel field and context are both`)\n\t_ = rp.Cancel                       //@ diag(re`deprecated since Go 1\\.7:.+If a Request's Cancel field and context are both`)\n\t_ = syscall.StringByteSlice(\"\")     //@ diag(`Use ByteSliceFromString instead`)\n\t_ = os.SEEK_SET                     //@ diag(`Use io.SeekStart, io.SeekCurrent, and io.SeekEnd`)\n\t_ = os.SEEK_CUR                     //@ diag(`Use io.SeekStart, io.SeekCurrent, and io.SeekEnd`)\n\t_ = os.SEEK_END                     //@ diag(`Use io.SeekStart, io.SeekCurrent, and io.SeekEnd`)\n\tif err == http.ErrWriteAfterFlush { //@ diag(`ErrWriteAfterFlush is no longer`)\n\t\tprintln()\n\t}\n\tvar _ flate.ReadError //@ diag(`No longer returned`)\n\n\tvar tr *http.Transport\n\ttr.CancelRequest(nil) //@ diag(`CancelRequest has been deprecated`)\n\n\tvar conn driver.Conn\n\tconn.Begin() //@ diag(`Begin has been deprecated`)\n\n\t_ = x509.CertificateRequest{}.Attributes //@ diag(`(crypto/x509.CertificateRequest).Attributes has been deprecated since Go 1.5 and an alternative has been available since Go 1.3:`)\n}\n\n// Deprecated: Don't use this.\nfunc fn2() {\n\t_ = syscall.StringByteSlice(\"\")\n\n\tanon := func(x int) {\n\t\tprintln(x)\n\t\t_ = syscall.StringByteSlice(\"\")\n\n\t\tanon := func(x int) {\n\t\t\tprintln(x)\n\t\t\t_ = syscall.StringByteSlice(\"\")\n\t\t}\n\t\tanon(2)\n\t}\n\tanon(1)\n}\n"
  },
  {
    "path": "staticcheck/sa1020/sa1020.go",
    "content": "package sa1020\n\nimport (\n\t\"go/constant\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1020\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(checkListenAddressRules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Using an invalid host:port pair with a \\'net.Listen\\'-related function`,\n\t\tText: `Functions such as \\'net.Listen\\', \\'net.ListenTCP\\', and similar,\nexpect a valid network address in the form of host:port. The host, the port,\nor both, can be omitted, e.g. \\'localhost:8080\\', \\':8080\\' or \\':\\' are valid\nhost:port pairs.\nSee https://pkg.go.dev/net#Listen for the full documentation.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkListenAddressRules = map[string]callcheck.Check{\n\t\"net/http.ListenAndServe\":    checkValidHostPort(0),\n\t\"net/http.ListenAndServeTLS\": checkValidHostPort(0),\n}\n\nfunc checkValidHostPort(arg int) callcheck.Check {\n\treturn func(call *callcheck.Call) {\n\t\tif !ValidHostPort(call.Args[arg].Value) {\n\t\t\tconst MsgInvalidHostPort = \"invalid port or service name in host:port pair\"\n\t\t\tcall.Args[arg].Invalid(MsgInvalidHostPort)\n\t\t}\n\t}\n}\n\nfunc ValidHostPort(v callcheck.Value) bool {\n\tif k := callcheck.ExtractConstExpectKind(v, constant.String); k != nil {\n\t\ts := constant.StringVal(k.Value)\n\t\tif s == \"\" {\n\t\t\treturn true\n\t\t}\n\t\t_, port, err := net.SplitHostPort(s)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\t// TODO(dh): check hostname\n\t\tif !validatePort(port) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc validateServiceName(s string) bool {\n\tif len(s) < 1 || len(s) > 15 {\n\t\treturn false\n\t}\n\tif s[0] == '-' || s[len(s)-1] == '-' {\n\t\treturn false\n\t}\n\tif strings.Contains(s, \"--\") {\n\t\treturn false\n\t}\n\thasLetter := false\n\tfor _, r := range s {\n\t\tif (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {\n\t\t\thasLetter = true\n\t\t\tcontinue\n\t\t}\n\t\tif r >= '0' && r <= '9' {\n\t\t\tcontinue\n\t\t}\n\t\treturn false\n\t}\n\treturn hasLetter\n}\n\nfunc validatePort(s string) bool {\n\tn, err := strconv.ParseInt(s, 10, 64)\n\tif err != nil {\n\t\treturn validateServiceName(s)\n\t}\n\treturn n >= 0 && n <= 65535\n}\n"
  },
  {
    "path": "staticcheck/sa1020/sa1020_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1020\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1020/testdata/go1.0/CheckListenAddress/CheckListenAddress.go",
    "content": "package pkg\n\nimport \"net/http\"\n\nfunc fn() {\n\t// Seen in actual code\n\thttp.ListenAndServe(\"localhost:8080/\", nil) //@ diag(`invalid port or service name in host:port pair`)\n\thttp.ListenAndServe(\"localhost\", nil)       //@ diag(`invalid port or service name in host:port pair`)\n\thttp.ListenAndServe(\"localhost:8080\", nil)\n\thttp.ListenAndServe(\":8080\", nil)\n\thttp.ListenAndServe(\":http\", nil)\n\thttp.ListenAndServe(\"localhost:http\", nil)\n\thttp.ListenAndServe(\"local_host:8080\", nil)\n\thttp.ListenAndServe(\"\", nil) // providing no address at all makes it default to :http\n}\n"
  },
  {
    "path": "staticcheck/sa1021/sa1021.go",
    "content": "package sa1021\n\nimport (\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1021\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Using \\'bytes.Equal\\' to compare two \\'net.IP\\'`,\n\t\tText: `A \\'net.IP\\' stores an IPv4 or IPv6 address as a slice of bytes. The\nlength of the slice for an IPv4 address, however, can be either 4 or\n16 bytes long, using different ways of representing IPv4 addresses. In\norder to correctly compare two \\'net.IP\\'s, the \\'net.IP.Equal\\' method should\nbe used, as it takes both representations into account.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"bytes.Equal\": func(call *callcheck.Call) {\n\t\tif isConvertedFrom(call.Args[knowledge.Arg(\"bytes.Equal.a\")].Value, \"net.IP\") &&\n\t\t\tisConvertedFrom(call.Args[knowledge.Arg(\"bytes.Equal.b\")].Value, \"net.IP\") {\n\t\t\tcall.Invalid(\"use net.IP.Equal to compare net.IPs, not bytes.Equal\")\n\t\t}\n\t},\n}\n\n// ConvertedFrom reports whether value v was converted from type typ.\nfunc isConvertedFrom(v callcheck.Value, typ string) bool {\n\tchange, ok := v.Value.(*ir.ChangeType)\n\treturn ok && types.TypeString(types.Unalias(change.X.Type()), nil) == typ\n}\n"
  },
  {
    "path": "staticcheck/sa1021/sa1021_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1021\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1021/testdata/go1.0/CheckBytesEqualIP/CheckBytesEqualIP.go",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n\t\"net\"\n)\n\nfunc fn() {\n\ttype T []byte\n\tvar i1, i2 net.IP\n\tvar b1, b2 []byte\n\tvar t1, t2 T\n\n\tbytes.Equal(i1, i2) //@ diag(`use net.IP.Equal to compare net.IPs, not bytes.Equal`)\n\tbytes.Equal(b1, b2)\n\tbytes.Equal(t1, t2)\n\n\tbytes.Equal(i1, b1)\n\tbytes.Equal(b1, i1)\n}\n"
  },
  {
    "path": "staticcheck/sa1023/sa1023.go",
    "content": "package sa1023\n\nimport (\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1023\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Modifying the buffer in an \\'io.Writer\\' implementation`,\n\t\tText:     `\\'Write\\' must not modify the slice data, even temporarily.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// TODO(dh): this might be a good candidate for taint analysis.\n\t// Taint the argument as MUST_NOT_MODIFY, then propagate that\n\t// through functions like bytes.Split\n\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tsig := fn.Signature\n\t\tif fn.Name() != \"Write\" || sig.Recv() == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif !types.Identical(sig, knowledge.Signatures[\"(io.Writer).Write\"]) {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, block := range fn.Blocks {\n\t\t\tfor _, ins := range block.Instrs {\n\t\t\t\tswitch ins := ins.(type) {\n\t\t\t\tcase *ir.Store:\n\t\t\t\t\taddr, ok := ins.Addr.(*ir.IndexAddr)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif addr.X != fn.Params[1] {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\treport.Report(pass, ins, \"io.Writer.Write must not modify the provided buffer, not even temporarily\")\n\t\t\t\tcase *ir.Call:\n\t\t\t\t\tif !irutil.IsCallTo(ins.Common(), \"append\") {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif ins.Common().Args[0] != fn.Params[1] {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\treport.Report(pass, ins, \"io.Writer.Write must not modify the provided buffer, not even temporarily\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1023/sa1023_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1023\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1023/testdata/go1.0/CheckWriterBufferModified/CheckWriterBufferModified.go",
    "content": "package pkg\n\ntype T1 struct{}\ntype T2 struct{}\ntype T3 struct{}\ntype T4 struct{}\ntype T5 struct{}\ntype T6 struct{}\ntype T7 struct{}\n\ntype Bytes []byte\n\nfunc (T1) Write(b []byte) (int, error) {\n\tb = append(b, '\\n') //@ diag(`io.Writer.Write must not modify the provided buffer`)\n\t_ = b\n\treturn 0, nil\n}\n\nfunc (T2) Write(b []byte) (int, error) {\n\tb[0] = 0 //@ diag(`io.Writer.Write must not modify the provided buffer`)\n\treturn 0, nil\n}\n\nfunc (T3) Write(b []byte) string {\n\tb[0] = 0\n\treturn \"\"\n}\n\nfunc (T4) Write(b []byte, r byte) (int, error) {\n\tb[0] = r\n\treturn 0, nil\n}\n\nfunc (T7) Write(b Bytes) (int, error) {\n\tb[0] = 0\n\treturn 0, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1023/testdata/go1.9/CheckWriterBufferModified/CheckWriterBufferModified.go",
    "content": "package pkg\n\ntype T5 struct{}\ntype T6 struct{}\n\ntype AliasByte = byte\ntype AliasByteSlice = []byte\ntype AliasInt = int\ntype AliasError = error\n\nfunc (T5) Write(b []AliasByte) (int, error) {\n\tb[0] = 0 //@ diag(`io.Writer.Write must not modify the provided buffer`)\n\treturn 0, nil\n}\n\nfunc (T6) Write(b AliasByteSlice) (AliasInt, AliasError) {\n\tb[0] = 0 //@ diag(`io.Writer.Write must not modify the provided buffer`)\n\treturn 0, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1024/sa1024.go",
    "content": "package sa1024\n\nimport (\n\t\"go/constant\"\n\t\"sort\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1024\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `A string cutset contains duplicate characters`,\n\t\tText: `The \\'strings.TrimLeft\\' and \\'strings.TrimRight\\' functions take cutsets, not\nprefixes. A cutset is treated as a set of characters to remove from a\nstring. For example,\n\n    strings.TrimLeft(\"42133word\", \"1234\")\n\nwill result in the string \\'\"word\"\\' – any characters that are 1, 2, 3 or\n4 are cut from the left of the string.\n\nIn order to remove one string from another, use \\'strings.TrimPrefix\\' instead.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"strings.Trim\":      check,\n\t\"strings.TrimLeft\":  check,\n\t\"strings.TrimRight\": check,\n}\n\nfunc check(call *callcheck.Call) {\n\targ := call.Args[1]\n\tif !isUniqueStringCutset(arg.Value) {\n\t\tconst MsgNonUniqueCutset = \"cutset contains duplicate characters\"\n\t\targ.Invalid(MsgNonUniqueCutset)\n\t}\n}\n\nfunc isUniqueStringCutset(v callcheck.Value) bool {\n\tif c := callcheck.ExtractConstExpectKind(v, constant.String); c != nil {\n\t\ts := constant.StringVal(c.Value)\n\t\trs := runeSlice(s)\n\t\tif len(rs) < 2 {\n\t\t\treturn true\n\t\t}\n\t\tsort.Sort(rs)\n\t\tfor i, r := range rs[1:] {\n\t\t\tif rs[i] == r {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\treturn true\n}\n\ntype runeSlice []rune\n\nfunc (rs runeSlice) Len() int               { return len(rs) }\nfunc (rs runeSlice) Less(i int, j int) bool { return rs[i] < rs[j] }\nfunc (rs runeSlice) Swap(i int, j int)      { rs[i], rs[j] = rs[j], rs[i] }\n"
  },
  {
    "path": "staticcheck/sa1024/sa1024_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1024\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1024/testdata/go1.0/CheckNonUniqueCutset/CheckNonUniqueCutset.go",
    "content": "package pkg\n\nimport \"strings\"\n\nfunc fn(s string) {\n\t_ = strings.TrimLeft(s, \"\")\n\t_ = strings.TrimLeft(s, \"a\")\n\t_ = strings.TrimLeft(s, \"µ\")\n\t_ = strings.TrimLeft(s, \"abc\")\n\t_ = strings.TrimLeft(s, \"http://\") //@ diag(`duplicate characters`)\n}\n"
  },
  {
    "path": "staticcheck/sa1025/sa1025.go",
    "content": "package sa1025\n\nimport (\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1025\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `It is not possible to use \\'(*time.Timer).Reset\\''s return value correctly`,\n\t\tSince:    \"2019.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tfor _, block := range fn.Blocks {\n\t\t\tfor _, ins := range block.Instrs {\n\t\t\t\tcall, ok := ins.(*ir.Call)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !irutil.IsCallTo(call.Common(), \"(*time.Timer).Reset\") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\trefs := call.Referrers()\n\t\t\t\tif refs == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfor _, ref := range irutil.FilterDebug(*refs) {\n\t\t\t\t\tifstmt, ok := ref.(*ir.If)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tfound := false\n\t\t\t\t\tfor _, succ := range ifstmt.Block().Succs {\n\t\t\t\t\t\tif len(succ.Preds) != 1 {\n\t\t\t\t\t\t\t// Merge point, not a branch in the\n\t\t\t\t\t\t\t// syntactical sense.\n\n\t\t\t\t\t\t\t// FIXME(dh): this is broken for if\n\t\t\t\t\t\t\t// statements a la \"if x || y\"\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tirutil.Walk(succ, func(b *ir.BasicBlock) bool {\n\t\t\t\t\t\t\tif !succ.Dominates(b) {\n\t\t\t\t\t\t\t\t// We've reached the end of the branch\n\t\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfor _, ins := range b.Instrs {\n\t\t\t\t\t\t\t\t// TODO(dh): we should check that we're receiving from the\n\t\t\t\t\t\t\t\t// channel of a time.Timer to further reduce false\n\t\t\t\t\t\t\t\t// positives. Not a key priority, considering the rarity\n\t\t\t\t\t\t\t\t// of Reset and the tiny likeliness of a false positive\n\t\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t\t// We intentionally don't handle aliases here, because\n\t\t\t\t\t\t\t\t// we're only interested in time.Timer.C.\n\t\t\t\t\t\t\t\tif ins, ok := ins.(*ir.Recv); ok && types.TypeString(ins.Chan.Type(), nil) == \"<-chan time.Time\" {\n\t\t\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\tif found {\n\t\t\t\t\t\treport.Report(pass, call, \"it is not possible to use Reset's return value correctly, as there is a race condition between draining the channel and the new timer expiring\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa1025/sa1025_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1025\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1025/testdata/go1.0/CheckTimerResetReturnValue/CheckTimerResetReturnValue.go",
    "content": "package fn\n\nimport \"time\"\n\nfunc fn1() {\n\tt := time.NewTimer(time.Second)\n\tt.Reset(time.Second)\n}\n\nfunc fn2() {\n\tt := time.NewTimer(time.Second)\n\t_ = t.Reset(time.Second)\n}\n\nfunc fn3() {\n\tt := time.NewTimer(time.Second)\n\tprintln(t.Reset(time.Second))\n}\n\nfunc fn4() {\n\tt := time.NewTimer(time.Second)\n\tif t.Reset(time.Second) {\n\t\tprintln(\"x\")\n\t}\n}\n\nfunc fn5() {\n\tt := time.NewTimer(time.Second)\n\tif t.Reset(time.Second) { //@ diag(`it is not possible to use Reset's return value correctly`)\n\t\t<-t.C\n\t}\n}\n\nfunc fn6(x bool) {\n\t// Not matched because we don't support complex boolean\n\t// expressions\n\tt := time.NewTimer(time.Second)\n\tif t.Reset(time.Second) || x {\n\t\t<-t.C\n\t}\n}\n\nfunc fn7(x bool) {\n\t// Not matched because we don't analyze that deeply\n\tt := time.NewTimer(time.Second)\n\ty := t.Reset(2 * time.Second)\n\tz := x || y\n\tprintln(z)\n\tif z {\n\t\t<-t.C\n\t}\n}\n\nfunc fn8() {\n\tt := time.NewTimer(time.Second)\n\tabc := t.Reset(time.Second) //@ diag(`it is not possible to use Reset's return value correctly`)\n\tif abc {\n\t\t<-t.C\n\t}\n}\n\nfunc fn9() {\n\tt := time.NewTimer(time.Second)\n\tif t.Reset(time.Second) {\n\t\tprintln(\"x\")\n\t}\n\t<-t.C\n}\n\nfunc fn10() {\n\tt := time.NewTimer(time.Second)\n\tif !t.Reset(time.Second) { //@ diag(`it is not possible to use Reset's return value correctly`)\n\t\t<-t.C\n\t}\n}\n\nfunc fn11(ch chan int) {\n\tt := time.NewTimer(time.Second)\n\tif !t.Reset(time.Second) {\n\t\t<-ch\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1026/sa1026.go",
    "content": "package sa1026\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/staticcheck/fakejson\"\n\t\"honnef.co/go/tools/staticcheck/fakexml\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1026\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Cannot marshal channels or functions`,\n\t\tSince:    \"2019.2\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"encoding/json.Marshal\":           checkJSON,\n\t\"encoding/xml.Marshal\":            checkXML,\n\t\"(*encoding/json.Encoder).Encode\": checkJSON,\n\t\"(*encoding/xml.Encoder).Encode\":  checkXML,\n}\n\nfunc checkJSON(call *callcheck.Call) {\n\targ := call.Args[0]\n\tT := arg.Value.Value.Type()\n\tif err := fakejson.Marshal(T); err != nil {\n\t\ttyp := types.TypeString(err.Type, types.RelativeTo(call.Parent.Pkg.Pkg))\n\t\tif err.Path == \"x\" {\n\t\t\targ.Invalid(fmt.Sprintf(\"trying to marshal unsupported type %s\", typ))\n\t\t} else {\n\t\t\targ.Invalid(fmt.Sprintf(\"trying to marshal unsupported type %s, via %s\", typ, err.Path))\n\t\t}\n\t}\n}\n\nfunc checkXML(call *callcheck.Call) {\n\targ := call.Args[0]\n\tT := arg.Value.Value.Type()\n\tif err := fakexml.Marshal(T); err != nil {\n\t\tswitch err := err.(type) {\n\t\tcase *fakexml.UnsupportedTypeError:\n\t\t\ttyp := types.TypeString(err.Type, types.RelativeTo(call.Parent.Pkg.Pkg))\n\t\t\tif err.Path == \"x\" {\n\t\t\t\targ.Invalid(fmt.Sprintf(\"trying to marshal unsupported type %s\", typ))\n\t\t\t} else {\n\t\t\t\targ.Invalid(fmt.Sprintf(\"trying to marshal unsupported type %s, via %s\", typ, err.Path))\n\t\t\t}\n\t\tcase *fakexml.CyclicTypeError:\n\t\t\ttyp := types.TypeString(err.Type, types.RelativeTo(call.Parent.Pkg.Pkg))\n\t\t\tif err.Path == \"x\" {\n\t\t\t\targ.Invalid(fmt.Sprintf(\"trying to marshal cyclic type %s\", typ))\n\t\t\t} else {\n\t\t\t\targ.Invalid(fmt.Sprintf(\"trying to marshal cyclic type %s, via %s\", typ, err.Path))\n\t\t\t}\n\t\tcase *fakexml.TagPathError:\n\t\t\t// Vet does a better job at reporting this error, because it can flag the actual struct tags, not just the call to Marshal\n\t\tdefault:\n\t\t\t// These errors get reported by SA5008 instead, which can flag the actual fields, independently of calls to xml.Marshal\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1026/sa1026_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1026\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1026/testdata/go1.0/CheckUnsupportedMarshal/CheckUnsupportedMarshal.go",
    "content": "package pkg\n\nimport (\n\t\"encoding/json\"\n\t\"encoding/xml\"\n\t\"time\"\n)\n\ntype T1 struct {\n\tA int\n\tB func() `json:\"-\" xml:\"-\"`\n\tc chan int\n}\n\ntype T2 struct {\n\tT1\n}\n\ntype T3 struct {\n\tCh chan int\n}\n\ntype T4 struct {\n\tC ValueMarshaler\n}\n\ntype T5 struct {\n\tB func() `xml:\"-\"`\n}\n\ntype T6 struct {\n\tB func() `json:\"-\"`\n}\n\ntype T7 struct {\n\tA int\n\tB int\n\tT3\n}\n\ntype T8 struct {\n\tC int\n\t*T7\n}\n\ntype T9 struct {\n\tF PointerMarshaler\n}\n\ntype T10 struct {\n\tF *struct {\n\t\tPointerMarshaler\n\t}\n}\n\ntype Recursive struct {\n\tField *Recursive\n}\n\ntype ValueMarshaler chan int\n\nfunc (ValueMarshaler) MarshalText() ([]byte, error) { return nil, nil }\n\ntype PointerMarshaler chan int\n\nfunc (*PointerMarshaler) MarshalText() ([]byte, error) { return nil, nil }\n\nfunc fn() {\n\tvar t1 T1\n\tvar t2 T2\n\tvar t3 T3\n\tvar t4 T4\n\tvar t5 T5\n\tvar t6 T6\n\tvar t8 T8\n\tvar t9 T9\n\tvar t10 T10\n\tvar t11 Recursive\n\tjson.Marshal(t1)\n\tjson.Marshal(t2)\n\tjson.Marshal(t3) //@ diag(`unsupported type chan int, via x.Ch`)\n\tjson.Marshal(t4)\n\tjson.Marshal(t5) //@ diag(`unsupported type func(), via x.B`)\n\tjson.Marshal(t6)\n\t(*json.Encoder)(nil).Encode(t1)\n\t(*json.Encoder)(nil).Encode(t2)\n\t(*json.Encoder)(nil).Encode(t3) //@ diag(`unsupported type chan int, via x.Ch`)\n\t(*json.Encoder)(nil).Encode(t4)\n\t(*json.Encoder)(nil).Encode(t5) //@ diag(`unsupported type func(), via x.B`)\n\t(*json.Encoder)(nil).Encode(t6)\n\n\txml.Marshal(t1)\n\txml.Marshal(t2)\n\txml.Marshal(t3) //@ diag(`unsupported type chan int, via x.Ch`)\n\txml.Marshal(t4)\n\txml.Marshal(t5)\n\txml.Marshal(t6) //@ diag(`unsupported type func(), via x.B`)\n\t(*xml.Encoder)(nil).Encode(t1)\n\t(*xml.Encoder)(nil).Encode(t2)\n\t(*xml.Encoder)(nil).Encode(t3) //@ diag(`unsupported type chan int, via x.C`)\n\t(*xml.Encoder)(nil).Encode(t4)\n\t(*xml.Encoder)(nil).Encode(t5)\n\t(*xml.Encoder)(nil).Encode(t6) //@ diag(`unsupported type func(), via x.B`)\n\n\tjson.Marshal(t8)  //@ diag(`unsupported type chan int, via x.T7.T3.Ch`)\n\tjson.Marshal(t9)  //@ diag(`unsupported type PointerMarshaler, via x.F`)\n\tjson.Marshal(&t9) // this is fine, t9 is addressable, therefore T9.D is, too\n\tjson.Marshal(t10) // this is fine, T10.F.D is addressable\n\n\txml.Marshal(t8)  //@ diag(`unsupported type chan int, via x.T7.T3.Ch`)\n\txml.Marshal(t9)  //@ diag(`unsupported type PointerMarshaler, via x.F`)\n\txml.Marshal(&t9) // this is fine, t9 is addressable, therefore T9.D is, too\n\txml.Marshal(t10) // this is fine, T10.F.D is addressable\n\n\tjson.Marshal(t11)\n\txml.Marshal(t11)\n}\n\nfunc addressabilityJSON() {\n\tvar a PointerMarshaler\n\tvar b []PointerMarshaler\n\tvar c struct {\n\t\tF PointerMarshaler\n\t}\n\tvar d [4]PointerMarshaler\n\tjson.Marshal(a) //@ diag(re`unsupported type PointerMarshaler$`)\n\tjson.Marshal(&a)\n\tjson.Marshal(b)\n\tjson.Marshal(&b)\n\tjson.Marshal(c) //@ diag(`unsupported type PointerMarshaler, via x.F`)\n\tjson.Marshal(&c)\n\tjson.Marshal(d) //@ diag(`unsupported type PointerMarshaler, via x[0]`)\n\tjson.Marshal(&d)\n\n\tvar m1 map[string]PointerMarshaler\n\tjson.Marshal(m1)                                //@ diag(`unsupported type PointerMarshaler, via x[k]`)\n\tjson.Marshal(&m1)                               //@ diag(`unsupported type PointerMarshaler, via x[k]`)\n\tjson.Marshal([]map[string]PointerMarshaler{m1}) //@ diag(`unsupported type PointerMarshaler, via x[0][k]`)\n\n\tvar m2 map[string]*PointerMarshaler\n\tjson.Marshal(m2)\n\tjson.Marshal(&m2)\n\tjson.Marshal([]map[string]*PointerMarshaler{m2})\n}\n\nfunc addressabilityXML() {\n\tvar a PointerMarshaler\n\tvar b []PointerMarshaler\n\tvar c struct {\n\t\tXMLName xml.Name `json:\"foo\"`\n\t\tF       PointerMarshaler\n\t}\n\tvar d [4]PointerMarshaler\n\txml.Marshal(a) //@ diag(re`unsupported type PointerMarshaler$`)\n\txml.Marshal(&a)\n\txml.Marshal(b)\n\txml.Marshal(&b)\n\txml.Marshal(c) //@ diag(`unsupported type PointerMarshaler, via x.F`)\n\txml.Marshal(&c)\n\txml.Marshal(d) //@ diag(`unsupported type PointerMarshaler, via x[0]`)\n\txml.Marshal(&d)\n}\n\nfunc mapsJSON() {\n\tvar good map[int]string\n\tvar bad map[interface{}]string\n\t// the map key has to be statically known good; it must be a number or a string\n\tjson.Marshal(good)\n\tjson.Marshal(bad) //@ diag(`unsupported type map[interface{}]string`)\n\n\tvar m1 map[string]PointerMarshaler\n\tjson.Marshal(m1)                                //@ diag(`unsupported type PointerMarshaler, via x[k]`)\n\tjson.Marshal(&m1)                               //@ diag(`unsupported type PointerMarshaler, via x[k]`)\n\tjson.Marshal([]map[string]PointerMarshaler{m1}) //@ diag(`unsupported type PointerMarshaler, via x[0][k]`)\n\n\tvar m2 map[string]*PointerMarshaler\n\tjson.Marshal(m2)\n\tjson.Marshal(&m2)\n\tjson.Marshal([]map[string]*PointerMarshaler{m2})\n\n\tvar m3 map[string]ValueMarshaler\n\tjson.Marshal(m3)\n\tjson.Marshal(&m3)\n\tjson.Marshal([]map[string]ValueMarshaler{m3})\n\n\tvar m4 map[string]*ValueMarshaler\n\tjson.Marshal(m4)\n\tjson.Marshal(&m4)\n\tjson.Marshal([]map[string]*ValueMarshaler{m4})\n\n\tvar m5 map[ValueMarshaler]string\n\tvar m6 map[*ValueMarshaler]string\n\tvar m7 map[PointerMarshaler]string\n\tvar m8 map[*PointerMarshaler]string\n\n\tjson.Marshal(m5)\n\tjson.Marshal(m6)\n\tjson.Marshal(m7) //@ diag(`unsupported type map[PointerMarshaler]string`)\n\tjson.Marshal(m8)\n}\n\nfunc mapsXML() {\n\t// encoding/xml doesn't support any maps\n\tvar bad map[string]string\n\txml.Marshal(bad) //@ diag(`unsupported type`)\n}\n\nfunc fieldPriorityJSON() {\n\t// In this example, the channel doesn't matter, because T1.F has higher priority than T1.T2.F\n\ttype lT2 struct {\n\t\tF chan int\n\t}\n\ttype lT1 struct {\n\t\tF int\n\t\tlT2\n\t}\n\tjson.Marshal(lT1{})\n\n\t// In this example, it does matter\n\ttype lT4 struct {\n\t\tC chan int\n\t}\n\ttype lT3 struct {\n\t\tF int\n\t\tlT4\n\t}\n\tjson.Marshal(lT3{}) //@ diag(`unsupported type chan int, via x.lT4.C`)\n}\n\nfunc fieldPriorityXML() {\n\t// In this example, the channel doesn't matter, because T1.F has higher priority than T1.T2.F\n\ttype lT2 struct {\n\t\tF chan int\n\t}\n\ttype lT1 struct {\n\t\tF int\n\t\tlT2\n\t}\n\txml.Marshal(lT1{})\n\n\t// In this example, it does matter\n\ttype lT4 struct {\n\t\tC chan int\n\t}\n\ttype lT3 struct {\n\t\tF int\n\t\tlT4\n\t}\n\txml.Marshal(lT3{}) //@ diag(`unsupported type chan int, via x.lT4.C`)\n}\n\nfunc longPathJSON() {\n\tvar foo struct {\n\t\tField struct {\n\t\t\tField2 []struct {\n\t\t\t\tMap map[string]chan int\n\t\t\t}\n\t\t}\n\t}\n\tjson.Marshal(foo) //@ diag(`unsupported type chan int, via x.Field.Field2[0].Map[k]`)\n}\n\nfunc otherPackageJSON() {\n\tvar x time.Ticker\n\tjson.Marshal(x) //@ diag(`unsupported type <-chan time.Time, via x.C`)\n}\n\nfunc longPathXML() {\n\tvar foo struct {\n\t\tField struct {\n\t\t\tField2 []struct {\n\t\t\t\tMap map[string]chan int\n\t\t\t}\n\t\t}\n\t}\n\txml.Marshal(foo) //@ diag(`unsupported type map[string]chan int, via x.Field.Field2[0].Map`)\n}\n\nfunc otherPackageXML() {\n\tvar x time.Ticker\n\txml.Marshal(x) //@ diag(`unsupported type <-chan time.Time, via x.C`)\n}\n\ntype ToplevelPointerMarshalerXML struct {\n\tField map[string]string\n}\n\nfunc (*ToplevelPointerMarshalerXML) MarshalXML(*xml.Encoder, xml.StartElement) error {\n\treturn nil\n}\n\ntype ToplevelPointerMarshalerText struct {\n\tField map[string]string\n}\n\nfunc (*ToplevelPointerMarshalerText) MarshalText() ([]byte, error) {\n\treturn nil, nil\n}\n\nfunc toplevelPointer() {\n\txml.Marshal(&ToplevelPointerMarshalerXML{})\n\txml.Marshal(&ToplevelPointerMarshalerText{})\n\txml.Marshal(ToplevelPointerMarshalerXML{})  //@ diag(`unsupported type`)\n\txml.Marshal(ToplevelPointerMarshalerText{}) //@ diag(`unsupported type`)\n}\n\nfunc cyclicPointer() {\n\ttype P *P\n\ttype S2 struct {\n\t\tBar P\n\t}\n\ttype S1 struct {\n\t\tFoo S2\n\t}\n\tvar s S1\n\txml.Marshal(s) //@ diag(`cyclic type P, via x.Foo.Bar`)\n}\n\nfunc functionAsArgument(arg T1) {\n\t// https://staticcheck.io/issues/1660\n\n\tjson.Marshal(functionAsArgument) //@ diag(`unsupported type func(arg T1)`)\n\txml.Marshal(functionAsArgument)  //@ diag(`unsupported type func(arg T1)`)\n}\n"
  },
  {
    "path": "staticcheck/sa1026/testdata/go1.18/CheckUnsupportedMarshal/generics.go",
    "content": "package pkg\n\nimport (\n\t\"encoding/json\"\n\t\"encoding/xml\"\n)\n\ntype LMap[K comparable, V any] struct {\n\tM1 map[K]V\n\tM2 map[K]chan int\n}\n\nfunc (lm *LMap[K, V]) MarshalJSON() {\n\tjson.Marshal(lm.M1)\n\tjson.Marshal(lm.M2) //@ diag(`unsupported type`)\n}\n\nfunc recursiveGeneric() {\n\t// don't recurse infinitely\n\tvar t Tree[int]\n\tjson.Marshal(t)\n\txml.Marshal(t)\n}\n\ntype Tree[T any] struct {\n\tNode *Node[T]\n}\n\ntype Node[T any] struct {\n\tTree *Tree[T]\n}\n"
  },
  {
    "path": "staticcheck/sa1027/sa1027.go",
    "content": "package sa1027\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1027\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(checkAtomicAlignment),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Atomic access to 64-bit variable must be 64-bit aligned`,\n\t\tText: `On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to\narrange for 64-bit alignment of 64-bit words accessed atomically. The\nfirst word in a variable or in an allocated struct, array, or slice\ncan be relied upon to be 64-bit aligned.\n\nYou can use the structlayout tool to inspect the alignment of fields\nin a struct.`,\n\t\tSince:    \"2019.2\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkAtomicAlignment = map[string]callcheck.Check{\n\t\"sync/atomic.AddInt64\":             checkAtomicAlignmentImpl,\n\t\"sync/atomic.AddUint64\":            checkAtomicAlignmentImpl,\n\t\"sync/atomic.CompareAndSwapInt64\":  checkAtomicAlignmentImpl,\n\t\"sync/atomic.CompareAndSwapUint64\": checkAtomicAlignmentImpl,\n\t\"sync/atomic.LoadInt64\":            checkAtomicAlignmentImpl,\n\t\"sync/atomic.LoadUint64\":           checkAtomicAlignmentImpl,\n\t\"sync/atomic.StoreInt64\":           checkAtomicAlignmentImpl,\n\t\"sync/atomic.StoreUint64\":          checkAtomicAlignmentImpl,\n\t\"sync/atomic.SwapInt64\":            checkAtomicAlignmentImpl,\n\t\"sync/atomic.SwapUint64\":           checkAtomicAlignmentImpl,\n}\n\nfunc checkAtomicAlignmentImpl(call *callcheck.Call) {\n\tsizes := call.Pass.TypesSizes\n\tif sizes.Sizeof(types.Typ[types.Uintptr]) != 4 {\n\t\t// Not running on a 32-bit platform\n\t\treturn\n\t}\n\tv, ok := irutil.Flatten(call.Args[0].Value.Value).(*ir.FieldAddr)\n\tif !ok {\n\t\t// TODO(dh): also check indexing into arrays and slices\n\t\treturn\n\t}\n\tT := v.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct)\n\tfields := make([]*types.Var, 0, T.NumFields())\n\tfor i := 0; i < T.NumFields() && i <= v.Field; i++ {\n\t\tfields = append(fields, T.Field(i))\n\t}\n\n\toff := sizes.Offsetsof(fields)[v.Field]\n\tif off%8 != 0 {\n\t\tmsg := fmt.Sprintf(\"address of non 64-bit aligned field %s passed to %s\",\n\t\t\tT.Field(v.Field).Name(),\n\t\t\tirutil.CallName(call.Instr.Common()))\n\t\tcall.Invalid(msg)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1027/sa1027_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1027\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1027/testdata/go1.0/CheckAtomicAlignment/atomic32.go",
    "content": "// +build 386 arm armbe mips mipsle ppc s390 sparc riscv\n\npackage pkg\n\nimport \"sync/atomic\"\n\ntype T struct {\n\tA int64\n\tB int32\n\tC int64\n}\n\nfunc fn() {\n\tvar v T\n\tatomic.AddInt64(&v.A, 0)\n\tatomic.AddInt64(&v.C, 0) //@ diag(`address of non 64-bit aligned field C passed to sync/atomic.AddInt64`)\n\tatomic.LoadInt64(&v.C)   //@ diag(`address of non 64-bit aligned field C passed to sync/atomic.LoadInt64`)\n}\n\nfunc fn2(t *T) {\n\taddr := &t.C\n\tif true {\n\t\tatomic.LoadInt64(addr) //@ diag(`address of non 64-bit`)\n\t} else {\n\t\t_ = addr\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1027/testdata/go1.0/CheckAtomicAlignment/atomic64.go",
    "content": "// +build amd64 amd64p32 arm64 ppc64 ppc64le mips64 mips64le mips64p32 mips64p32le s390x sparc64 riscv64 loong64\n\npackage pkg\n\nimport \"sync/atomic\"\n\ntype T struct {\n\tA int64\n\tB int32\n\tC int64\n}\n\nfunc fn() {\n\tvar v T\n\tatomic.AddInt64(&v.A, 0)\n\tatomic.AddInt64(&v.C, 0)\n\tatomic.LoadInt64(&v.C)\n}\n"
  },
  {
    "path": "staticcheck/sa1028/sa1028.go",
    "content": "package sa1028\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1028\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `\\'sort.Slice\\' can only be used on slices`,\n\t\tText:     `The first argument of \\'sort.Slice\\' must be a slice.`,\n\t\tSince:    \"2020.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"sort.Slice\":         check,\n\t\"sort.SliceIsSorted\": check,\n\t\"sort.SliceStable\":   check,\n}\n\nfunc check(call *callcheck.Call) {\n\tc := call.Instr.Common().StaticCallee()\n\targ := call.Args[0]\n\n\tT := arg.Value.Value.Type().Underlying()\n\tswitch T.(type) {\n\tcase *types.Interface:\n\t\t// we don't know.\n\t\t// TODO(dh): if the value is a phi node we can look at its edges\n\t\tif k, ok := arg.Value.Value.(*ir.Const); ok && k.Value == nil {\n\t\t\t// literal nil, e.g. sort.Sort(nil, ...)\n\t\t\targ.Invalid(fmt.Sprintf(\"cannot call %s on nil literal\", c))\n\t\t}\n\tcase *types.Slice:\n\t\t// this is fine\n\tdefault:\n\t\t// this is not fine\n\t\targ.Invalid(fmt.Sprintf(\"%s must only be called on slices, was called on %s\", c, T))\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1028/sa1028_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1028\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1028/testdata/go1.0/CheckSortSlice/slice.go",
    "content": "package pkg\n\nimport \"sort\"\n\ntype T1 []int\ntype T2 T1\ntype T3 [1]int\ntype T4 string\n\nfunc fn(arg1 interface{}, arg2 []int) {\n\tvar v1 T1\n\tvar v2 T2\n\tvar v3 T3\n\tvar v4 T4\n\tvar v5 []int\n\tvar v6 interface{} = []int{}\n\tvar v7 interface{}\n\tif true {\n\t\tv7 = []int{}\n\t} else {\n\t\tv7 = 0\n\t}\n\tvar v8 interface{} = 0\n\tsort.Slice(arg1, nil)\n\tsort.Slice(arg2, nil)\n\tsort.Slice(v1, nil)\n\tsort.Slice(v2, nil)\n\tsort.Slice(v3, nil) //@ diag(`sort.Slice must only be called on slices, was called on [1]int`)\n\tsort.Slice(v4, nil) //@ diag(`sort.Slice must only be called on slices, was called on string`)\n\tsort.Slice(v5, nil)\n\tsort.Slice(v6, nil)\n\tsort.Slice(v7, nil)\n\tsort.Slice(v8, nil) //@ diag(`sort.Slice must only be called on slices, was called on int`)\n\tsort.Slice([]int{}, nil)\n\tsort.Slice(0, nil)         //@ diag(`sort.Slice must only be called on slices, was called on int`)\n\tsort.Slice(nil, nil)       //@ diag(`cannot call sort.Slice on nil literal`)\n\tsort.SliceIsSorted(0, nil) //@ diag(`sort.SliceIsSorted must only be called on slices, was called on int`)\n\tsort.SliceStable(0, nil)   //@ diag(`sort.SliceStable must only be called on slices, was called on int`)\n}\n"
  },
  {
    "path": "staticcheck/sa1029/sa1029.go",
    "content": "package sa1029\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1029\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(checkWithValueKeyRules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Inappropriate key in call to \\'context.WithValue\\'`,\n\t\tText: `The provided key must be comparable and should not be\nof type \\'string\\' or any other built-in type to avoid collisions between\npackages using context. Users of \\'WithValue\\' should define their own\ntypes for keys.\n\nTo avoid allocating when assigning to an \\'interface{}\\',\ncontext keys often have concrete type \\'struct{}\\'. Alternatively,\nexported context key variables' static type should be a pointer or\ninterface.`,\n\t\tSince:    \"2020.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkWithValueKeyRules = map[string]callcheck.Check{\n\t\"context.WithValue\": checkWithValueKey,\n}\n\nfunc checkWithValueKey(call *callcheck.Call) {\n\targ := call.Args[1]\n\tT := arg.Value.Value.Type()\n\tif typ, ok := types.Unalias(T).(*types.Basic); ok {\n\t\tif _, ok := T.(*types.Alias); ok {\n\t\t\targ.Invalid(\n\t\t\t\tfmt.Sprintf(\"should not use built-in type %s (via alias %s) as key for value; define your own type to avoid collisions\", typ, types.TypeString(T, types.RelativeTo(call.Pass.Pkg))))\n\t\t} else {\n\t\t\targ.Invalid(\n\t\t\t\tfmt.Sprintf(\"should not use built-in type %s as key for value; define your own type to avoid collisions\", typ))\n\t\t}\n\t}\n\t// TODO(dh): we should probably flag all anonymous structs, as they all risk collisions\n\tif s, ok := T.(*types.Struct); ok && s.NumFields() == 0 {\n\t\targ.Invalid(\"should not use empty anonymous struct as key for value; define your own type to avoid collisions\")\n\t} else if !types.Comparable(T) {\n\t\targ.Invalid(fmt.Sprintf(\"keys used with context.WithValue must be comparable, but type %s is not comparable\", T))\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1029/sa1029_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1029\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1029/testdata/go1.0/CheckWithValueKey/CheckWithValueKey.go",
    "content": "package pkg\n\nimport \"context\"\n\ntype T string\n\ntype T2 struct {\n\tA int\n}\n\ntype T3 struct {\n\tA []int\n}\n\nfunc fn(arg1 interface{}, arg2 string) {\n\tvar ctx context.Context\n\tcontext.WithValue(ctx, \"hi\", nil) //@ diag(`should not use built-in type string`)\n\tcontext.WithValue(ctx, arg1, nil)\n\tcontext.WithValue(ctx, arg2, nil) //@ diag(`should not use built-in type string`)\n\tv1 := interface{}(\"byte\")\n\tcontext.WithValue(ctx, v1, nil) //@ diag(`should not use built-in type string`)\n\n\tvar key T\n\tcontext.WithValue(ctx, key, nil)\n\tv2 := interface{}(key)\n\tcontext.WithValue(ctx, v2, nil)\n\tcontext.WithValue(ctx, T(\"\"), nil)\n\tcontext.WithValue(ctx, string(key), nil) //@ diag(`should not use built-in type string`)\n\n\tcontext.WithValue(ctx, []byte(nil), nil) //@ diag(`must be comparable`)\n\tcontext.WithValue(ctx, T2{}, nil)\n\tcontext.WithValue(ctx, T3{}, nil) //@ diag(`must be comparable`)\n\n\tcontext.WithValue(ctx, struct{ key string }{\"k\"}, nil)\n\tvar empty struct{}\n\tcontext.WithValue(ctx, struct{}{}, nil) //@ diag(`should not use empty anonymous struct`)\n\tcontext.WithValue(ctx, empty, nil)      //@ diag(`should not use empty anonymous struct`)\n}\n"
  },
  {
    "path": "staticcheck/sa1030/sa1030.go",
    "content": "package sa1030\n\nimport (\n\t\"fmt\"\n\t\"go/constant\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1030\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Invalid argument in call to a \\'strconv\\' function`,\n\t\tText: `This check validates the format, number base and bit size arguments of\nthe various parsing and formatting functions in \\'strconv\\'.`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"strconv.ParseComplex\": func(call *callcheck.Call) {\n\t\tvalidateComplexBitSize(call.Args[knowledge.Arg(\"strconv.ParseComplex.bitSize\")])\n\t},\n\t\"strconv.ParseFloat\": func(call *callcheck.Call) {\n\t\tvalidateFloatBitSize(call.Args[knowledge.Arg(\"strconv.ParseFloat.bitSize\")])\n\t},\n\t\"strconv.ParseInt\": func(call *callcheck.Call) {\n\t\tvalidateContinuousBitSize(call.Args[knowledge.Arg(\"strconv.ParseInt.bitSize\")], 0, 64)\n\t\tvalidateIntBaseAllowZero(call.Args[knowledge.Arg(\"strconv.ParseInt.base\")])\n\t},\n\t\"strconv.ParseUint\": func(call *callcheck.Call) {\n\t\tvalidateContinuousBitSize(call.Args[knowledge.Arg(\"strconv.ParseUint.bitSize\")], 0, 64)\n\t\tvalidateIntBaseAllowZero(call.Args[knowledge.Arg(\"strconv.ParseUint.base\")])\n\t},\n\n\t\"strconv.FormatComplex\": func(call *callcheck.Call) {\n\t\tvalidateComplexFormat(call.Args[knowledge.Arg(\"strconv.FormatComplex.fmt\")])\n\t\tvalidateComplexBitSize(call.Args[knowledge.Arg(\"strconv.FormatComplex.bitSize\")])\n\t},\n\t\"strconv.FormatFloat\": func(call *callcheck.Call) {\n\t\tvalidateFloatFormat(call.Args[knowledge.Arg(\"strconv.FormatFloat.fmt\")])\n\t\tvalidateFloatBitSize(call.Args[knowledge.Arg(\"strconv.FormatFloat.bitSize\")])\n\t},\n\t\"strconv.FormatInt\": func(call *callcheck.Call) {\n\t\tvalidateIntBase(call.Args[knowledge.Arg(\"strconv.FormatInt.base\")])\n\t},\n\t\"strconv.FormatUint\": func(call *callcheck.Call) {\n\t\tvalidateIntBase(call.Args[knowledge.Arg(\"strconv.FormatUint.base\")])\n\t},\n\n\t\"strconv.AppendFloat\": func(call *callcheck.Call) {\n\t\tvalidateFloatFormat(call.Args[knowledge.Arg(\"strconv.AppendFloat.fmt\")])\n\t\tvalidateFloatBitSize(call.Args[knowledge.Arg(\"strconv.AppendFloat.bitSize\")])\n\t},\n\t\"strconv.AppendInt\": func(call *callcheck.Call) {\n\t\tvalidateIntBase(call.Args[knowledge.Arg(\"strconv.AppendInt.base\")])\n\t},\n\t\"strconv.AppendUint\": func(call *callcheck.Call) {\n\t\tvalidateIntBase(call.Args[knowledge.Arg(\"strconv.AppendUint.base\")])\n\t},\n}\n\nfunc validateDiscreetBitSize(arg *callcheck.Argument, size1 int, size2 int) {\n\tif c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil {\n\t\tval, _ := constant.Int64Val(c.Value)\n\t\tif val != int64(size1) && val != int64(size2) {\n\t\t\targ.Invalid(fmt.Sprintf(\"'bitSize' argument is invalid, must be either %d or %d\", size1, size2))\n\t\t}\n\t}\n}\n\nfunc validateComplexBitSize(arg *callcheck.Argument) { validateDiscreetBitSize(arg, 64, 128) }\nfunc validateFloatBitSize(arg *callcheck.Argument)   { validateDiscreetBitSize(arg, 32, 64) }\n\nfunc validateContinuousBitSize(arg *callcheck.Argument, min int, max int) {\n\tif c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil {\n\t\tval, _ := constant.Int64Val(c.Value)\n\t\tif val < int64(min) || val > int64(max) {\n\t\t\targ.Invalid(fmt.Sprintf(\"'bitSize' argument is invalid, must be within %d and %d\", min, max))\n\t\t}\n\t}\n}\n\nfunc validateIntBase(arg *callcheck.Argument) {\n\tif c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil {\n\t\tval, _ := constant.Int64Val(c.Value)\n\t\tif val < 2 {\n\t\t\targ.Invalid(\"'base' must not be smaller than 2\")\n\t\t}\n\t\tif val > 36 {\n\t\t\targ.Invalid(\"'base' must not be larger than 36\")\n\t\t}\n\t}\n}\n\nfunc validateIntBaseAllowZero(arg *callcheck.Argument) {\n\tif c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil {\n\t\tval, _ := constant.Int64Val(c.Value)\n\t\tif val < 2 && val != 0 {\n\t\t\targ.Invalid(\"'base' must not be smaller than 2, unless it is 0\")\n\t\t}\n\t\tif val > 36 {\n\t\t\targ.Invalid(\"'base' must not be larger than 36\")\n\t\t}\n\t}\n}\n\nfunc validateComplexFormat(arg *callcheck.Argument) {\n\tvalidateFloatFormat(arg)\n}\n\nfunc validateFloatFormat(arg *callcheck.Argument) {\n\tif c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil {\n\t\tval, _ := constant.Int64Val(c.Value)\n\t\tswitch val {\n\t\tcase 'b', 'e', 'E', 'f', 'g', 'G', 'x', 'X':\n\t\tdefault:\n\t\t\targ.Invalid(fmt.Sprintf(\"'fmt' argument is invalid: unknown format %q\", val))\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1030/sa1030_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1030\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1030/testdata/go1.0/CheckStrconv/CheckStrconv.go",
    "content": "package pkg\n\nimport \"strconv\"\n\nfunc fn() {\n\tstrconv.ParseFloat(\"\", 16) //@ diag(`'bitSize' argument is invalid, must be either 32 or 64`)\n\tstrconv.ParseFloat(\"\", 32)\n\tstrconv.ParseFloat(\"\", 64)\n\tstrconv.ParseFloat(\"\", 128) //@ diag(`'bitSize' argument is invalid, must be either 32 or 64`)\n\n\tstrconv.ParseInt(\"\", 0, -1) //@ diag(`'bitSize' argument is invalid, must be within 0 and 64`)\n\tstrconv.ParseInt(\"\", 0, 0)\n\tstrconv.ParseInt(\"\", 0, 1)\n\tstrconv.ParseInt(\"\", 0, 64)\n\tstrconv.ParseInt(\"\", 0, 65) //@ diag(`'bitSize' argument is invalid, must be within 0 and 64`)\n\tstrconv.ParseInt(\"\", -1, 0) //@ diag(`'base' must not be smaller than 2, unless it is 0`)\n\tstrconv.ParseInt(\"\", 1, 0)  //@ diag(`'base' must not be smaller than 2, unless it is 0`)\n\tstrconv.ParseInt(\"\", 2, 0)\n\tstrconv.ParseInt(\"\", 10, 0)\n\tstrconv.ParseInt(\"\", 36, 0)\n\tstrconv.ParseInt(\"\", 37, 0) //@ diag(`'base' must not be larger than 36`)\n\n\tstrconv.ParseUint(\"\", 0, -1) //@ diag(`'bitSize' argument is invalid, must be within 0 and 64`)\n\tstrconv.ParseUint(\"\", 0, 0)\n\tstrconv.ParseUint(\"\", 0, 1)\n\tstrconv.ParseUint(\"\", 0, 64)\n\tstrconv.ParseUint(\"\", 0, 65) //@ diag(`'bitSize' argument is invalid, must be within 0 and 64`)\n\tstrconv.ParseUint(\"\", -1, 0) //@ diag(`'base' must not be smaller than 2, unless it is 0`)\n\tstrconv.ParseUint(\"\", 1, 0)  //@ diag(`'base' must not be smaller than 2, unless it is 0`)\n\tstrconv.ParseUint(\"\", 2, 0)\n\tstrconv.ParseUint(\"\", 10, 0)\n\tstrconv.ParseUint(\"\", 36, 0)\n\tstrconv.ParseUint(\"\", 37, 0) //@ diag(`'base' must not be larger than 36`)\n\n\tstrconv.FormatFloat(0, 'e', 0, 18) //@ diag(`'bitSize' argument is invalid, must be either 32 or 64`)\n\tstrconv.FormatFloat(0, 'e', 0, 32)\n\tstrconv.FormatFloat(0, 'e', 0, 64)\n\tstrconv.FormatFloat(0, 'e', 0, 128) //@ diag(`'bitSize' argument is invalid, must be either 32 or 64`)\n\tstrconv.FormatFloat(0, 'j', 0, 32)  //@ diag(`'fmt' argument is invalid: unknown format 'j'`)\n\n\tstrconv.FormatInt(0, 0) //@ diag(`'base' must not be smaller than 2`)\n\tstrconv.FormatInt(0, 1) //@ diag(`'base' must not be smaller than 2`)\n\tstrconv.FormatInt(0, 2)\n\tstrconv.FormatInt(0, 3)\n\tstrconv.FormatInt(0, 36)\n\tstrconv.FormatInt(0, 37) //@ diag(`'base' must not be larger than 36`)\n\n\tstrconv.FormatUint(0, 0) //@ diag(`'base' must not be smaller than 2`)\n\tstrconv.FormatUint(0, 1) //@ diag(`'base' must not be smaller than 2`)\n\tstrconv.FormatUint(0, 2)\n\tstrconv.FormatUint(0, 3)\n\tstrconv.FormatUint(0, 36)\n\tstrconv.FormatUint(0, 37) //@ diag(`'base' must not be larger than 36`)\n\n\tstrconv.AppendFloat(nil, 0, 'e', 0, 18) //@ diag(`'bitSize' argument is invalid, must be either 32 or 64`)\n\tstrconv.AppendFloat(nil, 0, 'e', 0, 32)\n\tstrconv.AppendFloat(nil, 0, 'e', 0, 64)\n\tstrconv.AppendFloat(nil, 0, 'e', 0, 128) //@ diag(`'bitSize' argument is invalid, must be either 32 or 64`)\n\tstrconv.AppendFloat(nil, 0, 'j', 0, 32)  //@ diag(`'fmt' argument is invalid: unknown format 'j'`)\n\n\tstrconv.AppendInt(nil, 0, 0) //@ diag(`'base' must not be smaller than 2`)\n\tstrconv.AppendInt(nil, 0, 1) //@ diag(`'base' must not be smaller than 2`)\n\tstrconv.AppendInt(nil, 0, 2)\n\tstrconv.AppendInt(nil, 0, 3)\n\tstrconv.AppendInt(nil, 0, 36)\n\tstrconv.AppendInt(nil, 0, 37) //@ diag(`'base' must not be larger than 36`)\n\n\tstrconv.AppendUint(nil, 0, 0) //@ diag(`'base' must not be smaller than 2`)\n\tstrconv.AppendUint(nil, 0, 1) //@ diag(`'base' must not be smaller than 2`)\n\tstrconv.AppendUint(nil, 0, 2)\n\tstrconv.AppendUint(nil, 0, 3)\n\tstrconv.AppendUint(nil, 0, 36)\n\tstrconv.AppendUint(nil, 0, 37) //@ diag(`'base' must not be larger than 36`)\n}\n"
  },
  {
    "path": "staticcheck/sa1030/testdata/go1.15/CheckStrconv/CheckStrconv.go",
    "content": "// +build go1.15\n\npackage pkg\n\nimport \"strconv\"\n\nfunc fn() {\n\tstrconv.ParseComplex(\"\", 32) //@ diag(`'bitSize' argument is invalid, must be either 64 or 128`)\n\tstrconv.ParseComplex(\"\", 64)\n\tstrconv.ParseComplex(\"\", 128)\n\tstrconv.ParseComplex(\"\", 256) //@ diag(`'bitSize' argument is invalid, must be either 64 or 128`)\n\n\tstrconv.FormatComplex(0, 'e', 0, 32) //@ diag(`'bitSize' argument is invalid, must be either 64 or 128`)\n\tstrconv.FormatComplex(0, 'e', 0, 64)\n\tstrconv.FormatComplex(0, 'e', 0, 128)\n\tstrconv.FormatComplex(0, 'e', 0, 256) //@ diag(`'bitSize' argument is invalid, must be either 64 or 128`)\n\tstrconv.FormatComplex(0, 'j', 0, 64)  //@ diag(`'fmt' argument is invalid: unknown format 'j'`)\n}\n"
  },
  {
    "path": "staticcheck/sa1030/testdata/go1.15/CheckStrconv/stub.go",
    "content": "package pkg\n"
  },
  {
    "path": "staticcheck/sa1031/sa1031.go",
    "content": "package sa1031\n\nimport (\n\t\"go/constant\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1031\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(checkEncodeRules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Overlapping byte slices passed to an encoder`,\n\t\tText: `In an encoding function of the form \\'Encode(dst, src)\\', \\'dst\\' and\n\\'src\\' were found to reference the same memory. This can result in\n\\'src\\' bytes being overwritten before they are read, when the encoder\nwrites more than one byte per \\'src\\' byte.`,\n\t\tSince:    \"2024.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkEncodeRules = map[string]callcheck.Check{\n\t\"encoding/ascii85.Encode\":            checkNonOverlappingDstSrc(knowledge.Arg(\"encoding/ascii85.Encode.dst\"), knowledge.Arg(\"encoding/ascii85.Encode.src\")),\n\t\"(*encoding/base32.Encoding).Encode\": checkNonOverlappingDstSrc(knowledge.Arg(\"(*encoding/base32.Encoding).Encode.dst\"), knowledge.Arg(\"(*encoding/base32.Encoding).Encode.src\")),\n\t\"(*encoding/base64.Encoding).Encode\": checkNonOverlappingDstSrc(knowledge.Arg(\"(*encoding/base64.Encoding).Encode.dst\"), knowledge.Arg(\"(*encoding/base64.Encoding).Encode.src\")),\n\t\"encoding/hex.Encode\":                checkNonOverlappingDstSrc(knowledge.Arg(\"encoding/hex.Encode.dst\"), knowledge.Arg(\"encoding/hex.Encode.src\")),\n}\n\nfunc checkNonOverlappingDstSrc(dstArg, srcArg int) callcheck.Check {\n\treturn func(call *callcheck.Call) {\n\t\tdst := call.Args[dstArg]\n\t\tsrc := call.Args[srcArg]\n\t\t_, dstConst := irutil.Flatten(dst.Value.Value).(*ir.Const)\n\t\t_, srcConst := irutil.Flatten(src.Value.Value).(*ir.Const)\n\t\tif dstConst || srcConst {\n\t\t\t// one of the arguments is nil, therefore overlap is not possible\n\t\t\treturn\n\t\t}\n\t\tif dst.Value == src.Value {\n\t\t\t// simple case of f(b, b)\n\t\t\tdst.Invalid(\"overlapping dst and src\")\n\t\t\treturn\n\t\t}\n\t\tdstSlice, ok := irutil.Flatten(dst.Value.Value).(*ir.Slice)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tsrcSlice, ok := irutil.Flatten(src.Value.Value).(*ir.Slice)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tif irutil.Flatten(dstSlice.X) != irutil.Flatten(srcSlice.X) {\n\t\t\t// differing underlying arrays, all is well\n\t\t\treturn\n\t\t}\n\t\tl1 := irutil.Flatten(dstSlice.Low)\n\t\tl2 := irutil.Flatten(srcSlice.Low)\n\t\tc1, ok1 := l1.(*ir.Const)\n\t\tc2, ok2 := l2.(*ir.Const)\n\t\tif l1 == l2 || (ok1 && ok2 && constant.Compare(c1.Value, token.EQL, c2.Value)) {\n\t\t\t// dst and src are the same slice, and have the same lower bound\n\t\t\tdst.Invalid(\"overlapping dst and src\")\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1031/sa1031_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1031\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1031/testdata/go1.0/CheckEncodingAscii85/CheckEncodingAscii85.go",
    "content": "package pkg\n\nimport (\n\t\"encoding/ascii85\"\n)\n\nfunc fn() {\n\tascii85.Encode(nil, nil)\n\tascii85.Encode(make([]byte, 0), nil)\n\tsliceA := make([]byte, 8)\n\tsliceB := make([]byte, 8)\n\tascii85.Encode(sliceA, sliceB)\n\tascii85.Encode(sliceA, sliceA) //@ diag(`overlapping dst and src`)\n\tascii85.Encode(sliceA[1:], sliceA[2:])\n\tascii85.Encode(sliceA[1:], sliceA[1:]) //@ diag(`overlapping dst and src`)\n\tsliceC := sliceA\n\tascii85.Encode(sliceA, sliceC) //@ diag(`overlapping dst and src`)\n\tif true {\n\t\tascii85.Encode(sliceA, sliceC) //@ diag(`overlapping dst and src`)\n\t}\n\tsliceD := sliceA[1:]\n\tsliceE := sliceA[1:]\n\tif true {\n\t\tascii85.Encode(sliceD, sliceE) //@ diag(`overlapping dst and src`)\n\t}\n}\n\nfunc fooSigmaA(a *[4]byte) {\n\tlow := 2\n\tx := a[low:]\n\n\tif true {\n\t\ty := a[low:]\n\t\tascii85.Encode(x, y) //@ diag(`overlapping dst and src`)\n\t}\n}\n\nfunc fooSigmaB(a *[4]byte) {\n\tx := a[:]\n\n\tif true {\n\t\ty := a[:]\n\t\tascii85.Encode(x, y) //@ diag(`overlapping dst and src`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1031/testdata/go1.0/CheckEncodingBase32/CheckEncodingBase32.go",
    "content": "package pkg\n\nimport (\n\t\"encoding/base32\"\n)\n\nfunc fn() {\n\tencoding := base32.StdEncoding\n\tencoding.Encode(nil, nil)\n\tencoding.Encode(make([]byte, 0), nil)\n\tsliceA := make([]byte, 8)\n\tsliceB := make([]byte, 8)\n\tencoding.Encode(sliceA, sliceB)\n\tencoding.Encode(sliceA, sliceA) //@ diag(`overlapping dst and src`)\n\tencoding.Encode(sliceA[1:], sliceA[2:])\n\tencoding.Encode(sliceA[1:], sliceA[1:]) //@ diag(`overlapping dst and src`)\n\tsliceC := sliceA\n\tencoding.Encode(sliceA, sliceC) //@ diag(`overlapping dst and src`)\n\tif true {\n\t\tencoding.Encode(sliceA, sliceC) //@ diag(`overlapping dst and src`)\n\t}\n\tsliceD := sliceA[1:]\n\tsliceE := sliceA[1:]\n\tif true {\n\t\tencoding.Encode(sliceD, sliceE) //@ diag(`overlapping dst and src`)\n\t}\n}\n\nfunc fooSigmaA(a *[4]byte) {\n\tencoding := base32.StdEncoding\n\tlow := 2\n\tx := a[low:]\n\n\tif true {\n\t\ty := a[low:]\n\t\tencoding.Encode(x, y) //@ diag(`overlapping dst and src`)\n\t}\n}\n\nfunc fooSigmaB(a *[4]byte) {\n\tencoding := base32.StdEncoding\n\tx := a[:]\n\n\tif true {\n\t\ty := a[:]\n\t\tencoding.Encode(x, y) //@ diag(`overlapping dst and src`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1031/testdata/go1.0/CheckEncodingBase64/CheckEncodingBase64.go",
    "content": "package pkg\n\nimport (\n\t\"encoding/base64\"\n)\n\nfunc fn() {\n\tencoding := base64.StdEncoding\n\tencoding.Encode(nil, nil)\n\tencoding.Encode(make([]byte, 0), nil)\n\tsliceA := make([]byte, 8)\n\tsliceB := make([]byte, 8)\n\tencoding.Encode(sliceA, sliceB)\n\tencoding.Encode(sliceA, sliceA) //@ diag(`overlapping dst and src`)\n\tencoding.Encode(sliceA[1:], sliceA[2:])\n\tencoding.Encode(sliceA[1:], sliceA[1:]) //@ diag(`overlapping dst and src`)\n\tsliceC := sliceA\n\tencoding.Encode(sliceA, sliceC) //@ diag(`overlapping dst and src`)\n\tif true {\n\t\tencoding.Encode(sliceA, sliceC) //@ diag(`overlapping dst and src`)\n\t}\n\tsliceD := sliceA[1:]\n\tsliceE := sliceA[1:]\n\tif true {\n\t\tencoding.Encode(sliceD, sliceE) //@ diag(`overlapping dst and src`)\n\t}\n}\n\nfunc fooSigmaA(a *[4]byte) {\n\tencoding := base64.StdEncoding\n\tlow := 2\n\tx := a[low:]\n\n\tif true {\n\t\ty := a[low:]\n\t\tencoding.Encode(x, y) //@ diag(`overlapping dst and src`)\n\t}\n}\n\nfunc fooSigmaB(a *[4]byte) {\n\tencoding := base64.StdEncoding\n\tx := a[:]\n\n\tif true {\n\t\ty := a[:]\n\t\tencoding.Encode(x, y) //@ diag(`overlapping dst and src`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1031/testdata/go1.0/CheckEncodingHex/CheckEncodingHex.go",
    "content": "package pkg\n\nimport (\n\t\"encoding/hex\"\n)\n\nfunc fn() {\n\thex.Encode(nil, nil)\n\thex.Encode(make([]byte, 0), nil)\n\tsliceA := make([]byte, 8)\n\tsliceB := make([]byte, 8)\n\thex.Encode(sliceA, sliceB)\n\thex.Encode(sliceA, sliceA) //@ diag(`overlapping dst and src`)\n\thex.Encode(sliceA[1:], sliceA[2:])\n\thex.Encode(sliceA[1:], sliceA[1:]) //@ diag(`overlapping dst and src`)\n\tsliceC := sliceA\n\thex.Encode(sliceA, sliceC) //@ diag(`overlapping dst and src`)\n\tif true {\n\t\thex.Encode(sliceA, sliceC) //@ diag(`overlapping dst and src`)\n\t}\n\tsliceD := sliceA[1:]\n\tsliceE := sliceA[1:]\n\tif true {\n\t\thex.Encode(sliceD, sliceE) //@ diag(`overlapping dst and src`)\n\t}\n\tvar b bool\n\tif !b && true {\n\t\thex.Encode(sliceD, sliceE) //@ diag(`overlapping dst and src`)\n\t}\n}\n\nfunc fooSigmaA(a *[4]byte) {\n\tlow := 2\n\tx := a[low:]\n\n\tif true {\n\t\ty := a[low:]\n\t\thex.Encode(x, y) //@ diag(`overlapping dst and src`)\n\t}\n}\n\nfunc fooSigmaB(a *[4]byte) {\n\tx := a[:]\n\n\tif true {\n\t\ty := a[:]\n\t\thex.Encode(x, y) //@ diag(`overlapping dst and src`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1032/sa1032.go",
    "content": "package sa1032\n\nimport (\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA1032\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Wrong order of arguments to \\'errors.Is\\'`,\n\t\tText: `\nThe first argument of the function \\'errors.Is\\' is the error\nthat we have and the second argument is the error we're trying to match against.\nFor example:\n\n\tif errors.Is(err, io.EOF) { ... }\n\nThis check detects some cases where the two arguments have been swapped. It\nflags any calls where the first argument is referring to a package-level error\nvariable, such as\n\n\tif errors.Is(io.EOF, err) { /* this is wrong */ }`,\n\t\tSince:    \"2024.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"errors.Is\": validateIs,\n}\n\nfunc validateIs(call *callcheck.Call) {\n\tif len(call.Args) != 2 {\n\t\treturn\n\t}\n\n\tglobal := func(arg *callcheck.Argument) *ir.Global {\n\t\tv, ok := arg.Value.Value.(*ir.Load)\n\t\tif !ok {\n\t\t\treturn nil\n\t\t}\n\t\tg, _ := v.X.(*ir.Global)\n\t\treturn g\n\t}\n\n\tx, y := call.Args[0], call.Args[1]\n\tgx := global(x)\n\tif gx == nil {\n\t\treturn\n\t}\n\n\tif pkgx := gx.Package().Pkg; pkgx != nil && pkgx.Path() != call.Pass.Pkg.Path() {\n\t\t// x is a global that's not in this package\n\n\t\tif gy := global(y); gy != nil {\n\t\t\tif pkgy := gy.Package().Pkg; pkgy != nil && pkgy.Path() != call.Pass.Pkg.Path() {\n\t\t\t\t// Both arguments refer to globals that aren't in this package. This can\n\t\t\t\t// genuinely happen for external tests that check that one error \"is\"\n\t\t\t\t// another one. net/http's external tests, for example, do\n\t\t\t\t// `errors.Is(http.ErrNotSupported, errors.ErrUnsupported)`.\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tcall.Invalid(\"arguments have the wrong order\")\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa1032/sa1032_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa1032\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa1032/testdata/go1.0/example.com/ErrorsOrder/ErrorsOrder.go",
    "content": "package errors\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"io/fs\"\n)\n\nvar gErr = errors.New(\"global\")\n\ntype myErr struct{}\n\nfunc (myErr) Error() string { return \"\" }\n\nfunc is() {\n\terr := errors.New(\"oh noes\")\n\n\t_ = errors.Is(err, fs.ErrNotExist)\n\t_ = errors.Is(fs.ErrNotExist, err) //@ diag(`wrong order`)\n\tif errors.Is(err, fs.ErrNotExist) {\n\t}\n\tif errors.Is(fs.ErrNotExist, err) { //@ diag(`wrong order`)\n\t}\n\n\t_ = errors.Is(gErr, fs.ErrNotExist)\n\t_ = errors.Is(fs.ErrNotExist, gErr) //@ diag(`wrong order`)\n\tif errors.Is(gErr, fs.ErrNotExist) {\n\t}\n\tif errors.Is(fs.ErrNotExist, gErr) { //@ diag(`wrong order`)\n\t}\n\n\t_ = errors.Is(myErr{}, fs.ErrNotExist)\n\t_ = errors.Is(fs.ErrNotExist, myErr{}) //@ diag(`wrong order`)\n\tif errors.Is(myErr{}, fs.ErrNotExist) {\n\t}\n\tif errors.Is(fs.ErrNotExist, myErr{}) { //@ diag(`wrong order`)\n\t}\n\n\t_ = errors.Is(&myErr{}, fs.ErrNotExist)\n\t_ = errors.Is(fs.ErrNotExist, &myErr{}) //@ diag(`wrong order`)\n\tif errors.Is(&myErr{}, fs.ErrNotExist) {\n\t}\n\tif errors.Is(fs.ErrNotExist, &myErr{}) { //@ diag(`wrong order`)\n\t}\n\n\tif !errors.Is(io.EOF, fs.ErrNotExist) {\n\t\t// If both arguments are globals there's no right or wrong order.\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa2000/sa2000.go",
    "content": "package sa2000\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA2000\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `\\'(*sync.WaitGroup).Add\\' called inside the goroutine, leading to a race condition`,\n\t\tText: `\\'(*sync.WaitGroup).Add\\' must be called before starting the goroutine\nit is meant to wait for. Calling \\'Add\\' inside the goroutine creates a race\ncondition between the call to \\'Add\\' and the call to \\'Wait\\'.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkWaitgroupAddQ = pattern.MustParse(`\n\t(GoStmt\n\t\t(CallExpr\n\t\t\t(FuncLit\n\t\t\t\t_\n\t\t\t\tcall@(CallExpr (Symbol \"(*sync.WaitGroup).Add\") _):_) _))`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, m := range code.Matches(pass, checkWaitgroupAddQ) {\n\t\tcall := m.State[\"call\"].(ast.Node)\n\t\treport.Report(pass, call, fmt.Sprintf(\"should call %s before starting the goroutine to avoid a race\", report.Render(pass, call)))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa2000/sa2000_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa2000\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa2000/testdata/go1.0/CheckWaitgroupAdd/CheckWaitgroupAdd.go",
    "content": "package pkg\n\nimport (\n\t\"sync\"\n)\n\nfunc fn() {\n\twg := sync.WaitGroup{}\n\twg.Add(1)\n\tgo func() {\n\t\twg.Done()\n\t}()\n\n\tgo func() {\n\t\twg.Add(1) //@ diag(`should call wg.Add(1) before starting`)\n\t\twg.Done()\n\t}()\n\n\twg.Add(1)\n\tgo func(wg sync.WaitGroup) {\n\t\twg.Done()\n\t}(wg)\n\n\twg.Add(1)\n\tgo func(wg *sync.WaitGroup) {\n\t\twg.Done()\n\t}(&wg)\n\n\twg.Wait()\n}\n\nfunc fn2(wg sync.WaitGroup) {\n\twg.Add(1)\n}\n"
  },
  {
    "path": "staticcheck/sa2001/sa2001.go",
    "content": "package sa2001\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA2001\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Empty critical section, did you mean to defer the unlock?`,\n\t\tText: `Empty critical sections of the kind\n\n    mu.Lock()\n    mu.Unlock()\n\nare very often a typo, and the following was intended instead:\n\n    mu.Lock()\n    defer mu.Unlock()\n\nDo note that sometimes empty critical sections can be useful, as a\nform of signaling to wait on another goroutine. Many times, there are\nsimpler ways of achieving the same effect. When that isn't the case,\nthe code should be amply commented to avoid confusion. Combining such\ncomments with a \\'//lint:ignore\\' directive can be used to suppress this\nrare false positive.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tif pass.Pkg.Path() == \"sync_test\" {\n\t\t// exception for the sync package's tests\n\t\treturn nil, nil\n\t}\n\n\t// Initially it might seem like this check would be easier to\n\t// implement using IR. After all, we're only checking for two\n\t// consecutive method calls. In reality, however, there may be any\n\t// number of other instructions between the lock and unlock, while\n\t// still constituting an empty critical section. For example,\n\t// given `m.x().Lock(); m.x().Unlock()`, there will be a call to\n\t// x(). In the AST-based approach, this has a tiny potential for a\n\t// false positive (the second call to x might be doing work that\n\t// is protected by the mutex). In an IR-based approach, however,\n\t// it would miss a lot of real bugs.\n\n\tmutexParams := func(s ast.Stmt) (x ast.Expr, funcName string, ok bool) {\n\t\texpr, ok := s.(*ast.ExprStmt)\n\t\tif !ok {\n\t\t\treturn nil, \"\", false\n\t\t}\n\t\tcall, ok := astutil.Unparen(expr.X).(*ast.CallExpr)\n\t\tif !ok {\n\t\t\treturn nil, \"\", false\n\t\t}\n\t\tsel, ok := call.Fun.(*ast.SelectorExpr)\n\t\tif !ok {\n\t\t\treturn nil, \"\", false\n\t\t}\n\n\t\tfn, ok := pass.TypesInfo.ObjectOf(sel.Sel).(*types.Func)\n\t\tif !ok {\n\t\t\treturn nil, \"\", false\n\t\t}\n\t\tsig := fn.Type().(*types.Signature)\n\t\tif sig.Params().Len() != 0 || sig.Results().Len() != 0 {\n\t\t\treturn nil, \"\", false\n\t\t}\n\n\t\treturn sel.X, fn.Name(), true\n\t}\n\n\tfn := func(node ast.Node) {\n\t\tblock := node.(*ast.BlockStmt)\n\t\tif len(block.List) < 2 {\n\t\t\treturn\n\t\t}\n\t\tfor i := range block.List[:len(block.List)-1] {\n\t\t\tsel1, method1, ok1 := mutexParams(block.List[i])\n\t\t\tsel2, method2, ok2 := mutexParams(block.List[i+1])\n\n\t\t\tif !ok1 || !ok2 || report.Render(pass, sel1) != report.Render(pass, sel2) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (method1 == \"Lock\" && method2 == \"Unlock\") ||\n\t\t\t\t(method1 == \"RLock\" && method2 == \"RUnlock\") {\n\t\t\t\treport.Report(pass, block.List[i+1], \"empty critical section\")\n\t\t\t}\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.BlockStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa2001/sa2001_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa2001\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa2001/testdata/go1.0/CheckEmptyCriticalSection/CheckEmptyCriticalSection.go",
    "content": "package pkg\n\nimport \"sync\"\n\nfunc fn1() {\n\tvar x sync.Mutex\n\tx.Lock()\n\tx.Unlock() //@ diag(`empty critical section`)\n}\n\nfunc fn2() {\n\tx := struct {\n\t\tm1 struct {\n\t\t\tm2 sync.Mutex\n\t\t}\n\t}{}\n\n\tx.m1.m2.Lock()\n\tx.m1.m2.Unlock() //@ diag(`empty critical section`)\n}\n\nfunc fn3() {\n\tvar x sync.RWMutex\n\tx.Lock()\n\tx.Unlock() //@ diag(`empty critical section`)\n\n\tx.RLock()\n\tx.RUnlock() //@ diag(`empty critical section`)\n\n\tx.Lock()\n\tdefer x.Unlock()\n}\n\nfunc fn4() {\n\tx := struct {\n\t\tm func() *sync.Mutex\n\t}{\n\t\tm: func() *sync.Mutex {\n\t\t\treturn new(sync.Mutex)\n\t\t},\n\t}\n\n\tx.m().Lock()\n\tx.m().Unlock() //@ diag(`empty critical section`)\n}\n\nfunc fn5() {\n\ti := 0\n\tvar x sync.Mutex\n\tx.Lock()\n\ti++\n\tx.Unlock()\n}\n\nfunc fn6() {\n\tx := &sync.Mutex{}\n\tx.Lock()\n\tx.Unlock() //@ diag(`empty critical section`)\n}\n\nfunc fn7() {\n\tx := &struct {\n\t\tsync.Mutex\n\t}{}\n\n\tx.Lock()\n\tx.Unlock() //@ diag(`empty critical section`)\n}\n\nfunc fn8() {\n\tvar x sync.Locker\n\tx = new(sync.Mutex)\n\n\tx.Lock()\n\tx.Unlock() //@ diag(`empty critical section`)\n}\n\nfunc fn9() {\n\tx := &struct {\n\t\tsync.Locker\n\t}{&sync.Mutex{}}\n\tx.Lock()\n\tx.Unlock() //@ diag(`empty critical section`)\n}\n\ntype T struct{}\n\nfunc (T) Lock() int { return 0 }\nfunc (T) Unlock()   {}\n\nfunc fn10() {\n\tvar t T\n\tt.Lock()\n\tt.Unlock()\n}\n"
  },
  {
    "path": "staticcheck/sa2002/sa2002.go",
    "content": "package sa2002\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA2002\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Called \\'testing.T.FailNow\\' or \\'SkipNow\\' in a goroutine, which isn't allowed`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tfor _, block := range fn.Blocks {\n\t\t\tfor _, ins := range block.Instrs {\n\t\t\t\tgostmt, ok := ins.(*ir.Go)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tvar fn *ir.Function\n\t\t\t\tswitch val := gostmt.Call.Value.(type) {\n\t\t\t\tcase *ir.Function:\n\t\t\t\t\tfn = val\n\t\t\t\tcase *ir.MakeClosure:\n\t\t\t\t\tfn = val.Fn.(*ir.Function)\n\t\t\t\tdefault:\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif fn.Blocks == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfor _, block := range fn.Blocks {\n\t\t\t\t\tfor _, ins := range block.Instrs {\n\t\t\t\t\t\tcall, ok := ins.(*ir.Call)\n\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif call.Call.IsInvoke() {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcallee := call.Call.StaticCallee()\n\t\t\t\t\t\tif callee == nil {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\trecv := callee.Signature.Recv()\n\t\t\t\t\t\tif recv == nil {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif !typeutil.IsPointerToTypeWithName(recv.Type(), \"testing.common\") {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfn, ok := call.Call.StaticCallee().Object().(*types.Func)\n\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tname := fn.Name()\n\t\t\t\t\t\tswitch name {\n\t\t\t\t\t\tcase \"FailNow\", \"Fatal\", \"Fatalf\", \"SkipNow\", \"Skip\", \"Skipf\":\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// TODO(dh): don't report multiple diagnostics\n\t\t\t\t\t\t// for multiple calls to T.Fatal, but do\n\t\t\t\t\t\t// collect all of them as related information\n\t\t\t\t\t\treport.Report(pass, gostmt, fmt.Sprintf(\"the goroutine calls T.%s, which must be called in the same goroutine as the test\", name),\n\t\t\t\t\t\t\treport.Related(call, fmt.Sprintf(\"call to T.%s\", name)))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa2002/sa2002_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa2002\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa2002/testdata/go1.0/CheckConcurrentTesting/CheckConcurrentTesting.go",
    "content": "package pkg\n\nimport \"testing\"\n\nfunc fn1() {\n\tvar t *testing.T\n\tgo func() { //@ diag(`the goroutine calls T.Fatal, which must be called in the same goroutine as the test`)\n\t\tt.Fatal()\n\t}()\n\tgo fn2(t) //@ diag(`the goroutine calls T.Fatal, which must be called in the same goroutine as the test`)\n\n\tfn := func() {\n\t\tt.Fatal()\n\t}\n\tgo fn() //@ diag(`the goroutine calls T.Fatal, which must be called in the same goroutine as the test`)\n}\n\nfunc fn2(t *testing.T) {\n\tt.Fatal()\n}\n\nfunc fn3(t *testing.T) {\n\tfn := func() {\n\t\tt.Fatal()\n\t}\n\tfn()\n}\n\nfunc fn4(t *testing.T) {\n\tt.Fatal()\n}\n\nfunc fn5(t *testing.T) {\n\tfunc() {\n\t\tt.Fatal()\n\t}()\n}\n"
  },
  {
    "path": "staticcheck/sa2003/sa2003.go",
    "content": "package sa2003\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA2003\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Deferred \\'Lock\\' right after locking, likely meant to defer \\'Unlock\\' instead`,\n\t\tText: `Deferring a call to \\'Lock\\' immediately after locking is almost always\na typo. For example:\n\n    mu.Lock()\n    defer mu.Lock()\n\nWhile this does not strictly guarantee a deadlock depending on how the\nsurrounding code is structured, it is highly likely to be a mistake.\nThe intended code was likely this:\n\n    mu.Lock()\n    defer mu.Unlock()`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tfor _, block := range fn.Blocks {\n\t\t\tinstrs := irutil.FilterDebug(block.Instrs)\n\t\t\tif len(instrs) < 2 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor i, ins := range instrs[:len(instrs)-1] {\n\t\t\t\tcall, ok := ins.(*ir.Call)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !irutil.IsCallToAny(call.Common(), \"(*sync.Mutex).Lock\", \"(*sync.RWMutex).RLock\") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tnins, ok := instrs[i+1].(*ir.Defer)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !irutil.IsCallToAny(&nins.Call, \"(*sync.Mutex).Lock\", \"(*sync.RWMutex).RLock\") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif call.Common().Args[0] != nins.Call.Args[0] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tname := shortCallName(call.Common())\n\t\t\t\talt := \"\"\n\t\t\t\tswitch name {\n\t\t\t\tcase \"Lock\":\n\t\t\t\t\talt = \"Unlock\"\n\t\t\t\tcase \"RLock\":\n\t\t\t\t\talt = \"RUnlock\"\n\t\t\t\t}\n\t\t\t\treport.Report(pass, nins, fmt.Sprintf(\"deferring %s right after having locked already; did you mean to defer %s?\", name, alt))\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n\nfunc shortCallName(call *ir.CallCommon) string {\n\tif call.IsInvoke() {\n\t\treturn \"\"\n\t}\n\tswitch v := call.Value.(type) {\n\tcase *ir.Function:\n\t\tfn, ok := v.Object().(*types.Func)\n\t\tif !ok {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn fn.Name()\n\tcase *ir.Builtin:\n\t\treturn v.Name()\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "staticcheck/sa2003/sa2003_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa2003\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa2003/testdata/go1.0/CheckDeferLock/CheckDeferLock.go",
    "content": "package pkg\n\nimport \"sync\"\n\nvar r sync.Mutex\nvar rw sync.RWMutex\n\nfunc fn1() {\n\tr.Lock()\n\tdefer r.Lock() //@ diag(`deferring Lock right after having locked already; did you mean to defer Unlock`)\n}\n\nfunc fn2() {\n\tr.Lock()\n\tdefer r.Unlock()\n}\n\nfunc fn3() {\n\tprintln(\"\")\n\tdefer r.Lock()\n}\n\nfunc fn4() {\n\trw.RLock()\n\tdefer rw.RLock() //@ diag(`deferring RLock right after having locked already; did you mean to defer RUnlock`)\n}\n\nfunc fn5() {\n\trw.RLock()\n\tdefer rw.Lock()\n}\n\nfunc fn6() {\n\tr.Lock()\n\tdefer rw.Lock()\n}\n"
  },
  {
    "path": "staticcheck/sa3000/sa3000.go",
    "content": "package sa3000\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\t\"go/version\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA3000\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `\\'TestMain\\' doesn't call \\'os.Exit\\', hiding test failures`,\n\t\tText: `Test executables (and in turn \\\"go test\\\") exit with a non-zero status\ncode if any tests failed. When specifying your own \\'TestMain\\' function,\nit is your responsibility to arrange for this, by calling \\'os.Exit\\' with\nthe correct code. The correct code is returned by \\'(*testing.M).Run\\', so\nthe usual way of implementing \\'TestMain\\' is to end it with\n\\'os.Exit(m.Run())\\'.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tvar (\n\t\tfnmain    ast.Node\n\t\tcallsExit bool\n\t\tcallsRun  bool\n\t\targ       types.Object\n\t)\n\tfn := func(node ast.Node, push bool) bool {\n\t\tif !push {\n\t\t\tif fnmain != nil && node == fnmain {\n\t\t\t\tif !callsExit && callsRun {\n\t\t\t\t\treport.Report(pass, fnmain, \"TestMain should call os.Exit to set exit code\")\n\t\t\t\t}\n\t\t\t\tfnmain = nil\n\t\t\t\tcallsExit = false\n\t\t\t\tcallsRun = false\n\t\t\t\targ = nil\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\n\t\tswitch node := node.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\tif fnmain != nil {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif !isTestMain(pass, node) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif version.Compare(code.StdlibVersion(pass, node), \"go1.15\") >= 0 {\n\t\t\t\t// Beginning with Go 1.15, the test framework will call\n\t\t\t\t// os.Exit for us.\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tfnmain = node\n\t\t\targ = pass.TypesInfo.ObjectOf(node.Type.Params.List[0].Names[0])\n\t\t\treturn true\n\t\tcase *ast.CallExpr:\n\t\t\tif code.IsCallTo(pass, node, \"os.Exit\") {\n\t\t\t\tcallsExit = true\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tsel, ok := node.Fun.(*ast.SelectorExpr)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tident, ok := sel.X.(*ast.Ident)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif arg != pass.TypesInfo.ObjectOf(ident) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif sel.Sel.Name == \"Run\" {\n\t\t\t\tcallsRun = true\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\tdefault:\n\t\t\tlint.ExhaustiveTypeSwitch(node)\n\t\t\treturn true\n\t\t}\n\t}\n\tpass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.FuncDecl)(nil), (*ast.CallExpr)(nil)}, fn)\n\treturn nil, nil\n}\n\nfunc isTestMain(pass *analysis.Pass, decl *ast.FuncDecl) bool {\n\tif decl.Name.Name != \"TestMain\" {\n\t\treturn false\n\t}\n\tif len(decl.Type.Params.List) != 1 {\n\t\treturn false\n\t}\n\targ := decl.Type.Params.List[0]\n\tif len(arg.Names) != 1 {\n\t\treturn false\n\t}\n\treturn code.IsOfPointerToTypeWithName(pass, arg.Type, \"testing.M\")\n}\n"
  },
  {
    "path": "staticcheck/sa3000/sa3000_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa3000\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa3000/testdata/go1.15/CheckTestMainExit-1/CheckTestMainExit-1.go",
    "content": "package pkg\n\nimport \"testing\"\n\nfunc TestMain(m *testing.M) {\n\tm.Run()\n}\n"
  },
  {
    "path": "staticcheck/sa3000/testdata/go1.4/CheckTestMainExit-1/CheckTestMainExit-1.go",
    "content": "package pkg\n\nimport \"testing\"\n\nfunc TestMain(m *testing.M) { //@ diag(`should call os.Exit`)\n\tm.Run()\n}\n"
  },
  {
    "path": "staticcheck/sa3000/testdata/go1.4/CheckTestMainExit-2/CheckTestMainExit-2.go",
    "content": "package pkg\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestMain(m *testing.M) {\n\tm.Run()\n\tos.Exit(1)\n}\n"
  },
  {
    "path": "staticcheck/sa3000/testdata/go1.4/CheckTestMainExit-3/CheckTestMainExit-3.go",
    "content": "package pkg\n\nimport \"testing\"\n\nfunc TestMain(t *testing.T) {\n}\n"
  },
  {
    "path": "staticcheck/sa3000/testdata/go1.4/CheckTestMainExit-4/CheckTestMainExit-4.go",
    "content": "package pkg\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc helper() { os.Exit(1) }\n\nfunc TestMain(m *testing.M) { //@ diag(`should call os.Exit`)\n\t// FIXME(dominikh): this is a false positive\n\tm.Run()\n\thelper()\n}\n"
  },
  {
    "path": "staticcheck/sa3000/testdata/go1.4/CheckTestMainExit-5/CheckTestMainExit-5.go",
    "content": "package pkg\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc helper(m *testing.M) { os.Exit(m.Run()) }\n\nfunc TestMain(m *testing.M) {\n\thelper(m)\n}\n"
  },
  {
    "path": "staticcheck/sa3001/sa3001.go",
    "content": "package sa3001\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA3001\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Assigning to \\'b.N\\' in benchmarks distorts the results`,\n\t\tText: `The testing package dynamically sets \\'b.N\\' to improve the reliability of\nbenchmarks and uses it in computations to determine the duration of a\nsingle operation. Benchmark code must not alter \\'b.N\\' as this would\nfalsify results.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar query = pattern.MustParse(`(AssignStmt sel@(SelectorExpr selX (Ident \"N\")) \"=\" [_] )`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, query) {\n\t\tassign := node.(*ast.AssignStmt)\n\t\tif !code.IsOfPointerToTypeWithName(pass, m.State[\"selX\"].(ast.Expr), \"testing.B\") {\n\t\t\tcontinue\n\t\t}\n\t\treport.Report(pass, assign,\n\t\t\tfmt.Sprintf(\"should not assign to %s\", report.Render(pass, m.State[\"sel\"])))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa3001/sa3001_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa3001\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa3001/testdata/go1.0/CheckBenchmarkN/CheckBenchmarkN.go",
    "content": "package foo\n\nimport \"testing\"\n\nfunc foo() {\n\tvar b *testing.B\n\tb.N = 1 //@ diag(`should not assign to b.N`)\n\t_ = b\n}\n"
  },
  {
    "path": "staticcheck/sa4000/sa4000.go",
    "content": "package sa4000\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"reflect\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n\t\"golang.org/x/tools/go/ast/edge\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4000\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Binary operator has identical expressions on both sides`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tvar isFloat func(T types.Type) bool\n\tisFloat = func(T types.Type) bool {\n\t\ttset := typeutil.NewTypeSet(T)\n\t\tif len(tset.Terms) == 0 {\n\t\t\t// no terms, so floats are a possibility\n\t\t\treturn true\n\t\t}\n\t\treturn tset.Any(func(term *types.Term) bool {\n\t\t\tswitch typ := term.Type().Underlying().(type) {\n\t\t\tcase *types.Basic:\n\t\t\t\tkind := typ.Kind()\n\t\t\t\treturn kind == types.Float32 || kind == types.Float64\n\t\t\tcase *types.Array:\n\t\t\t\treturn isFloat(typ.Elem())\n\t\t\tcase *types.Struct:\n\t\t\t\tfor field := range typ.Fields() {\n\t\t\t\t\tif isFloat(field.Type()) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\t\t})\n\t}\n\n\t// TODO(dh): this check ignores the existence of side-effects and\n\t// happily flags fn() == fn() – so far, we've had only two complains\n\t// about false positives, and it's caught several bugs in real\n\t// code.\n\t//\n\t// We special case functions from the math/rand package. Someone ran\n\t// into the following false positive: \"rand.Intn(2) - rand.Intn(2), which I wrote to generate values {-1, 0, 1} with {0.25, 0.5, 0.25} probability.\"\n\n\tskipComparableCheck := func(c inspector.Cursor) bool {\n\t\top, ok := c.Node().(*ast.BinaryExpr)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\t\tif clit, ok := op.X.(*ast.CompositeLit); !ok || len(clit.Elts) != 0 {\n\t\t\treturn false\n\t\t}\n\t\tif clit, ok := op.Y.(*ast.CompositeLit); !ok || len(clit.Elts) != 0 {\n\t\t\treturn false\n\t\t}\n\n\t\t// TODO(dh): we should probably skip ParenExprs, but users should\n\t\t// probably not use unnecessary ParenExprs.\n\t\tvspec, ok := c.Parent().Node().(*ast.ValueSpec)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\t\te, i := c.ParentEdge()\n\t\tif e != edge.ValueSpec_Values {\n\t\t\treturn false\n\t\t}\n\t\tif vspec.Names[i].Name == \"_\" {\n\t\t\t// `var _ = T{} == T{}` is permitted, as a compile-time\n\t\t\t// check that T implements comparable.\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\tfor c := range code.Cursor(pass).Preorder((*ast.BinaryExpr)(nil)) {\n\t\tnode := c.Node()\n\t\top := node.(*ast.BinaryExpr)\n\t\tswitch op.Op {\n\t\tcase token.EQL, token.NEQ:\n\t\t\tif skipComparableCheck(c) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\tcase token.SUB, token.QUO, token.AND, token.REM, token.OR, token.XOR, token.AND_NOT,\n\t\t\ttoken.LAND, token.LOR, token.LSS, token.GTR, token.LEQ, token.GEQ:\n\t\tdefault:\n\t\t\t// For some ops, such as + and *, it can make sense to\n\t\t\t// have identical operands\n\t\t\tcontinue\n\t\t}\n\n\t\tif isFloat(pass.TypesInfo.TypeOf(op.X)) {\n\t\t\t// 'float <op> float' makes sense for several operators.\n\t\t\t// We've tried keeping an exact list of operators to allow, but floats keep surprising us. Let's just give up instead.\n\t\t\tcontinue\n\t\t}\n\n\t\tif reflect.TypeOf(op.X) != reflect.TypeOf(op.Y) {\n\t\t\tcontinue\n\t\t}\n\t\tif report.Render(pass, op.X) != report.Render(pass, op.Y) {\n\t\t\tcontinue\n\t\t}\n\t\tl1, ok1 := op.X.(*ast.BasicLit)\n\t\tl2, ok2 := op.Y.(*ast.BasicLit)\n\t\tif ok1 && ok2 && l1.Kind == token.INT && l2.Kind == l1.Kind && l1.Value == \"0\" && l2.Value == l1.Value && code.IsGenerated(pass, l1.Pos()) {\n\t\t\t// cgo generates the following function call:\n\t\t\t// _cgoCheckPointer(_cgoBase0, 0 == 0) – it uses 0 == 0\n\t\t\t// instead of true in case the user shadowed the\n\t\t\t// identifier. Ideally we'd restrict this exception to\n\t\t\t// calls of _cgoCheckPointer, but it's not worth the\n\t\t\t// hassle of keeping track of the stack. <lit> <op> <lit>\n\t\t\t// are very rare to begin with, and we're mostly checking\n\t\t\t// for them to catch typos such as 1 == 1 where the user\n\t\t\t// meant to type i == 1. The odds of a false negative for\n\t\t\t// 0 == 0 are slim.\n\t\t\tcontinue\n\t\t}\n\n\t\tif expr, ok := op.X.(*ast.CallExpr); ok {\n\t\t\tcall := code.CallName(pass, expr)\n\t\t\tswitch call {\n\t\t\tcase \"math/rand.Int\",\n\t\t\t\t\"math/rand.Int31\",\n\t\t\t\t\"math/rand.Int31n\",\n\t\t\t\t\"math/rand.Int63\",\n\t\t\t\t\"math/rand.Int63n\",\n\t\t\t\t\"math/rand.Intn\",\n\t\t\t\t\"math/rand.Uint32\",\n\t\t\t\t\"math/rand.Uint64\",\n\t\t\t\t\"math/rand.ExpFloat64\",\n\t\t\t\t\"math/rand.Float32\",\n\t\t\t\t\"math/rand.Float64\",\n\t\t\t\t\"math/rand.NormFloat64\",\n\t\t\t\t\"(*math/rand.Rand).Int\",\n\t\t\t\t\"(*math/rand.Rand).Int31\",\n\t\t\t\t\"(*math/rand.Rand).Int31n\",\n\t\t\t\t\"(*math/rand.Rand).Int63\",\n\t\t\t\t\"(*math/rand.Rand).Int63n\",\n\t\t\t\t\"(*math/rand.Rand).Intn\",\n\t\t\t\t\"(*math/rand.Rand).Uint32\",\n\t\t\t\t\"(*math/rand.Rand).Uint64\",\n\t\t\t\t\"(*math/rand.Rand).ExpFloat64\",\n\t\t\t\t\"(*math/rand.Rand).Float32\",\n\t\t\t\t\"(*math/rand.Rand).Float64\",\n\t\t\t\t\"(*math/rand.Rand).NormFloat64\",\n\t\t\t\t\"math/rand/v2.Int\",\n\t\t\t\t\"math/rand/v2.Int32\",\n\t\t\t\t\"math/rand/v2.Int32N\",\n\t\t\t\t\"math/rand/v2.Int64\",\n\t\t\t\t\"math/rand/v2.Int64N\",\n\t\t\t\t\"math/rand/v2.IntN\",\n\t\t\t\t\"math/rand/v2.N\",\n\t\t\t\t\"math/rand/v2.Uint\",\n\t\t\t\t\"math/rand/v2.Uint32\",\n\t\t\t\t\"math/rand/v2.Uint32N\",\n\t\t\t\t\"math/rand/v2.Uint64\",\n\t\t\t\t\"math/rand/v2.Uint64N\",\n\t\t\t\t\"math/rand/v2.UintN\",\n\t\t\t\t\"math/rand/v2.ExpFloat64\",\n\t\t\t\t\"math/rand/v2.Float32\",\n\t\t\t\t\"math/rand/v2.Float64\",\n\t\t\t\t\"math/rand/v2.NormFloat64\",\n\t\t\t\t\"(*math/rand/v2.Rand).Int\",\n\t\t\t\t\"(*math/rand/v2.Rand).Int32\",\n\t\t\t\t\"(*math/rand/v2.Rand).Int32N\",\n\t\t\t\t\"(*math/rand/v2.Rand).Int64\",\n\t\t\t\t\"(*math/rand/v2.Rand).Int64N\",\n\t\t\t\t\"(*math/rand/v2.Rand).IntN\",\n\t\t\t\t\"(*math/rand/v2.Rand).N\",\n\t\t\t\t\"(*math/rand/v2.Rand).Uint\",\n\t\t\t\t\"(*math/rand/v2.Rand).Uint32\",\n\t\t\t\t\"(*math/rand/v2.Rand).Uint32N\",\n\t\t\t\t\"(*math/rand/v2.Rand).Uint64\",\n\t\t\t\t\"(*math/rand/v2.Rand).Uint64N\",\n\t\t\t\t\"(*math/rand/v2.Rand).UintN\",\n\t\t\t\t\"(*math/rand/v2.Rand).ExpFloat64\",\n\t\t\t\t\"(*math/rand/v2.Rand).Float32\",\n\t\t\t\t\"(*math/rand/v2.Rand).Float64\",\n\t\t\t\t\"(*math/rand/v2.Rand).NormFloat64\":\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\treport.Report(pass, op, fmt.Sprintf(\"identical expressions on the left and right side of the '%s' operator\", op.Op))\n\t}\n\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4000/sa4000_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4000\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4000/testdata/go1.0/CheckLhsRhsIdentical/CheckLhsRhsIdentical.go",
    "content": "package pkg\n\nimport \"math/rand\"\n\ntype Float float64\n\ntype Floats [5]float64\ntype Ints [5]int\n\ntype T1 struct {\n\tA float64\n\tB float64\n}\n\ntype T2 struct {\n\tA float64\n\tB int\n}\n\nfunc fn(a int, s []int, f1 float64, f2 Float, fs Floats, is Ints, t1 T1, t2 T2) {\n\tif 0 == 0 { //@ diag(`identical expressions`)\n\t\tprintln()\n\t}\n\tif 1 == 1 { //@ diag(`identical expressions`)\n\t\tprintln()\n\t}\n\tif a == a { //@ diag(`identical expressions`)\n\t\tprintln()\n\t}\n\tif a != a { //@ diag(`identical expressions`)\n\t\tprintln()\n\t}\n\tif s[0] == s[0] { //@ diag(`identical expressions`)\n\t\tprintln()\n\t}\n\tif 1&1 == 1 { //@ diag(`identical expressions`)\n\t\tprintln()\n\t}\n\tif (1 + 2 + 3) == (1 + 2 + 3) { //@ diag(`identical expressions`)\n\t\tprintln()\n\t}\n\tif f1 == f1 {\n\t\tprintln()\n\t}\n\tif f1 != f1 {\n\t\tprintln()\n\t}\n\tif f1 > f1 {\n\t\tprintln()\n\t}\n\tif f1-f1 == 0 {\n\t\tprintln()\n\t}\n\tif f2 == f2 {\n\t\tprintln()\n\t}\n\tif fs == fs {\n\t\tprintln()\n\t}\n\tif is == is { //@ diag(`identical expressions`)\n\t\tprintln()\n\t}\n\tif t1 == t1 {\n\t\tprintln()\n\t}\n\tif t2 == t2 {\n\t\tprintln()\n\t}\n}\n\nfunc fn2() {\n\t_ = rand.Int() - rand.Int()\n\t_ = rand.Int31() - rand.Int31()\n\t_ = rand.Int31n(0) - rand.Int31n(0)\n\t_ = rand.Int63() - rand.Int63()\n\t_ = rand.Int63n(0) - rand.Int63n(0)\n\t_ = rand.Intn(0) - rand.Intn(0)\n\t_ = rand.Uint32() - rand.Uint32()\n\t_ = rand.Uint64() - rand.Uint64()\n\t_ = rand.ExpFloat64() - rand.ExpFloat64()\n\t_ = rand.Float32() - rand.Float32()\n\t_ = rand.Float64() - rand.Float64()\n\t_ = rand.NormFloat64() - rand.NormFloat64()\n\n\tvar rng *rand.Rand\n\t_ = rng.Int() - rng.Int()\n\t_ = rng.Int31() - rng.Int31()\n\t_ = rng.Int31n(0) - rng.Int31n(0)\n\t_ = rng.Int63() - rng.Int63()\n\t_ = rng.Int63n(0) - rng.Int63n(0)\n\t_ = rng.Intn(0) - rng.Intn(0)\n\t_ = rng.Uint32() - rng.Uint32()\n\t_ = rng.Uint64() - rng.Uint64()\n\t_ = rng.ExpFloat64() - rng.ExpFloat64()\n\t_ = rng.Float32() - rng.Float32()\n\t_ = rng.Float64() - rng.Float64()\n\t_ = rng.NormFloat64() - rng.NormFloat64()\n\n\t// not flagged; the return value is an interface and might be a float.\n\t_ = rand.NewSource(0) == rand.NewSource(0)\n}\n\ntype T struct{ X int }\n\n// Not flagged, used as a compile-time assertion that T is comparable\nvar _ = T{} == T{}\n\n// Flagged, the presence of initializers is dubious\nvar _ = T{1} == T{1} //@ diag(`identical expressions`)\n"
  },
  {
    "path": "staticcheck/sa4000/testdata/go1.0/CheckLhsRhsIdentical/cgo.go",
    "content": "package pkg\n\n// void foo(void **p) {}\nimport \"C\"\nimport \"unsafe\"\n\nfunc Foo() {\n\tvar p unsafe.Pointer\n\n\tC.foo(&p)\n\tif 0 == 0 {\n\t\t// We don't currently flag this instance of 0 == 0 because of\n\t\t// our cgo-specific exception.\n\t\tprintln()\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4000/testdata/go1.18/CheckLhsRhsIdentical/generics.go",
    "content": "package pkg\n\nfunc tpfn1[T comparable](x T) {\n\tif x != x {\n\t}\n}\n\nfunc tpfn2[T int | string](x T) {\n\tif x != x { //@ diag(`identical expressions`)\n\t}\n}\n\nfunc tpfn3[T int | float64](x T) {\n\tif x != x {\n\t}\n}\n\nfunc tpfn4[E int | int64, T [4]E](x T) {\n\tif x != x { //@ diag(`identical expressions`)\n\t}\n}\n\nfunc tpfn5[E int | float64, T [4]E](x T) {\n\tif x != x {\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4000/testdata/go1.22/CheckLhsRhsIdentical/randv2.go",
    "content": "package pkg\n\nimport \"math/rand/v2\"\n\nfunc fn() {\n\t_ = rand.N[uint](5) == rand.N[uint](5)\n}\n"
  },
  {
    "path": "staticcheck/sa4001/sa4001.go",
    "content": "package sa4001\n\nimport (\n\t\"go/ast\"\n\t\"regexp\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4001\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `\\'&*x\\' gets simplified to \\'x\\', it does not copy \\'x\\'`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\t// cgo produces code like fn(&*_Cvar_kSomeCallbacks) which we don't\n\t// want to flag.\n\tcgoIdent               = regexp.MustCompile(`^_C(func|var)_.+$`)\n\tcheckIneffectiveCopyQ1 = pattern.MustParse(`(UnaryExpr \"&\" (StarExpr obj))`)\n\tcheckIneffectiveCopyQ2 = pattern.MustParse(`(StarExpr (UnaryExpr \"&\" _))`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tif m, ok := code.Match(pass, checkIneffectiveCopyQ1, node); ok {\n\t\t\tif ident, ok := m.State[\"obj\"].(*ast.Ident); !ok || !cgoIdent.MatchString(ident.Name) {\n\t\t\t\treport.Report(pass, node, \"&*x will be simplified to x. It will not copy x.\")\n\t\t\t}\n\t\t} else if _, ok := code.Match(pass, checkIneffectiveCopyQ2, node); ok {\n\t\t\treport.Report(pass, node, \"*&x will be simplified to x. It will not copy x.\")\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.UnaryExpr)(nil), (*ast.StarExpr)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4001/sa4001_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4001\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4001/testdata/go1.0/CheckIneffectiveCopy/CheckIneffectiveCopy.go",
    "content": "package pkg\n\ntype T struct{}\n\nfunc fn1(_ *T) {}\n\nfunc fn2() {\n\tt1 := &T{}\n\tfn1(&*t1) //@ diag(`will not copy`)\n\tfn1(*&t1) //@ diag(`will not copy`)\n\n\t_Cvar_something := &T{}\n\tfn1(&*_Cvar_something)\n}\n"
  },
  {
    "path": "staticcheck/sa4003/sa4003.go",
    "content": "package sa4003\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"math\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4003\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Comparing unsigned values against negative values is pointless`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tisobj := func(expr ast.Expr, name string) bool {\n\t\tif name == \"\" {\n\t\t\treturn false\n\t\t}\n\t\tsel, ok := expr.(*ast.SelectorExpr)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\t\treturn typeutil.IsObject(pass.TypesInfo.ObjectOf(sel.Sel), name)\n\t}\n\n\tfn := func(node ast.Node) {\n\t\texpr := node.(*ast.BinaryExpr)\n\t\ttx := pass.TypesInfo.TypeOf(expr.X)\n\t\tbasic, ok := tx.Underlying().(*types.Basic)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\t// We only check for the math constants and integer literals, not for all constant expressions. This is to avoid\n\t\t// false positives when constant values differ under different build tags.\n\t\tvar (\n\t\t\tmaxMathConst string\n\t\t\tminMathConst string\n\t\t\tmaxLiteral   constant.Value\n\t\t\tminLiteral   constant.Value\n\t\t)\n\n\t\tswitch basic.Kind() {\n\t\tcase types.Uint8:\n\t\t\tmaxMathConst = \"math.MaxUint8\"\n\t\t\tminLiteral = constant.MakeUint64(0)\n\t\t\tmaxLiteral = constant.MakeUint64(math.MaxUint8)\n\t\tcase types.Uint16:\n\t\t\tmaxMathConst = \"math.MaxUint16\"\n\t\t\tminLiteral = constant.MakeUint64(0)\n\t\t\tmaxLiteral = constant.MakeUint64(math.MaxUint16)\n\t\tcase types.Uint32:\n\t\t\tmaxMathConst = \"math.MaxUint32\"\n\t\t\tminLiteral = constant.MakeUint64(0)\n\t\t\tmaxLiteral = constant.MakeUint64(math.MaxUint32)\n\t\tcase types.Uint64:\n\t\t\tmaxMathConst = \"math.MaxUint64\"\n\t\t\tminLiteral = constant.MakeUint64(0)\n\t\t\tmaxLiteral = constant.MakeUint64(math.MaxUint64)\n\t\tcase types.Uint:\n\t\t\t// TODO(dh): we could chose 32 bit vs 64 bit depending on the file's build tags\n\t\t\tmaxMathConst = \"math.MaxUint64\"\n\t\t\tminLiteral = constant.MakeUint64(0)\n\t\t\tmaxLiteral = constant.MakeUint64(math.MaxUint64)\n\n\t\tcase types.Int8:\n\t\t\tminMathConst = \"math.MinInt8\"\n\t\t\tmaxMathConst = \"math.MaxInt8\"\n\t\t\tminLiteral = constant.MakeInt64(math.MinInt8)\n\t\t\tmaxLiteral = constant.MakeInt64(math.MaxInt8)\n\t\tcase types.Int16:\n\t\t\tminMathConst = \"math.MinInt16\"\n\t\t\tmaxMathConst = \"math.MaxInt16\"\n\t\t\tminLiteral = constant.MakeInt64(math.MinInt16)\n\t\t\tmaxLiteral = constant.MakeInt64(math.MaxInt16)\n\t\tcase types.Int32:\n\t\t\tminMathConst = \"math.MinInt32\"\n\t\t\tmaxMathConst = \"math.MaxInt32\"\n\t\t\tminLiteral = constant.MakeInt64(math.MinInt32)\n\t\t\tmaxLiteral = constant.MakeInt64(math.MaxInt32)\n\t\tcase types.Int64:\n\t\t\tminMathConst = \"math.MinInt64\"\n\t\t\tmaxMathConst = \"math.MaxInt64\"\n\t\t\tminLiteral = constant.MakeInt64(math.MinInt64)\n\t\t\tmaxLiteral = constant.MakeInt64(math.MaxInt64)\n\t\tcase types.Int:\n\t\t\t// TODO(dh): we could chose 32 bit vs 64 bit depending on the file's build tags\n\t\t\tminMathConst = \"math.MinInt64\"\n\t\t\tmaxMathConst = \"math.MaxInt64\"\n\t\t\tminLiteral = constant.MakeInt64(math.MinInt64)\n\t\t\tmaxLiteral = constant.MakeInt64(math.MaxInt64)\n\t\t}\n\n\t\tisLiteral := func(expr ast.Expr, c constant.Value) bool {\n\t\t\tif c == nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn code.IsIntegerLiteral(pass, expr, c)\n\t\t}\n\t\tisZeroLiteral := func(expr ast.Expr) bool {\n\t\t\treturn code.IsIntegerLiteral(pass, expr, constant.MakeInt64(0))\n\t\t}\n\n\t\tif (expr.Op == token.GTR || expr.Op == token.GEQ) && (isobj(expr.Y, maxMathConst) || isLiteral(expr.Y, maxLiteral)) ||\n\t\t\t(expr.Op == token.LSS || expr.Op == token.LEQ) && (isobj(expr.X, maxMathConst) || isLiteral(expr.X, maxLiteral)) {\n\t\t\treport.Report(pass, expr, fmt.Sprintf(\"no value of type %s is greater than %s\", basic, maxMathConst), report.FilterGenerated())\n\t\t}\n\n\t\tif expr.Op == token.LEQ && (isobj(expr.Y, maxMathConst) || isLiteral(expr.Y, maxLiteral)) ||\n\t\t\texpr.Op == token.GEQ && (isobj(expr.X, maxMathConst) || isLiteral(expr.X, maxLiteral)) {\n\t\t\treport.Report(pass, expr, fmt.Sprintf(\"every value of type %s is <= %s\", basic, maxMathConst), report.FilterGenerated())\n\t\t}\n\n\t\tif (basic.Info() & types.IsUnsigned) != 0 {\n\t\t\tif (expr.Op == token.LSS && isZeroLiteral(expr.Y)) ||\n\t\t\t\t(expr.Op == token.GTR && isZeroLiteral(expr.X)) {\n\t\t\t\treport.Report(pass, expr, fmt.Sprintf(\"no value of type %s is less than 0\", basic), report.FilterGenerated())\n\t\t\t}\n\t\t\tif expr.Op == token.GEQ && isZeroLiteral(expr.Y) ||\n\t\t\t\texpr.Op == token.LEQ && isZeroLiteral(expr.X) {\n\t\t\t\treport.Report(pass, expr, fmt.Sprintf(\"every value of type %s is >= 0\", basic), report.FilterGenerated())\n\t\t\t}\n\t\t} else {\n\t\t\tif (expr.Op == token.LSS || expr.Op == token.LEQ) && (isobj(expr.Y, minMathConst) || isLiteral(expr.Y, minLiteral)) ||\n\t\t\t\t(expr.Op == token.GTR || expr.Op == token.GEQ) && (isobj(expr.X, minMathConst) || isLiteral(expr.X, minLiteral)) {\n\t\t\t\treport.Report(pass, expr, fmt.Sprintf(\"no value of type %s is less than %s\", basic, minMathConst), report.FilterGenerated())\n\t\t\t}\n\t\t\tif expr.Op == token.GEQ && (isobj(expr.Y, minMathConst) || isLiteral(expr.Y, minLiteral)) ||\n\t\t\t\texpr.Op == token.LEQ && (isobj(expr.X, minMathConst) || isLiteral(expr.X, minLiteral)) {\n\t\t\t\treport.Report(pass, expr, fmt.Sprintf(\"every value of type %s is >= %s\", basic, minMathConst), report.FilterGenerated())\n\t\t\t}\n\t\t}\n\n\t}\n\tcode.Preorder(pass, fn, (*ast.BinaryExpr)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4003/sa4003_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4003\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4003/testdata/go1.0/CheckExtremeComparison/CheckExtremeComparison.go",
    "content": "package pkg\n\nimport \"math\"\n\nfunc fn1() {\n\tvar (\n\t\tu8  uint8\n\t\tu16 uint16\n\t\tu   uint\n\n\t\ti8  int8\n\t\ti16 int16\n\t\ti   int\n\t)\n\n\t_ = u8 > math.MaxUint8  //@ diag(`no value of type uint8 is greater than math.MaxUint8`)\n\t_ = u8 >= math.MaxUint8 //@ diag(`no value of type uint8 is greater than math.MaxUint8`)\n\t_ = u8 >= 0             //@ diag(`every value of type uint8 is >= 0`)\n\t_ = u8 <= math.MaxUint8 //@ diag(`every value of type uint8 is <= math.MaxUint8`)\n\t_ = u8 > 0\n\t_ = u8 >= 1\n\t_ = u8 < math.MaxUint8\n\t_ = u8 < 0 //@ diag(`no value of type uint8 is less than 0`)\n\t_ = u8 <= 0\n\t_ = 0 > u8 //@ diag(`no value of type uint8 is less than 0`)\n\t_ = 0 >= u8\n\n\t_ = u16 > math.MaxUint8\n\t_ = u16 > math.MaxUint16 //@ diag(`no value of type uint16 is greater than math.MaxUint16`)\n\t_ = u16 <= math.MaxUint8\n\t_ = u16 <= math.MaxUint16 //@ diag(`every value of type uint16 is <= math.MaxUint16`)\n\n\t_ = u > math.MaxUint32\n\n\t_ = i8 > math.MaxInt8 //@ diag(`no value of type int8 is greater than math.MaxInt8`)\n\t_ = i16 > math.MaxInt8\n\t_ = i16 > math.MaxInt16 //@ diag(`no value of type int16 is greater than math.MaxInt16`)\n\t_ = i > math.MaxInt32\n\t_ = i8 < 0\n\t_ = i8 <= math.MinInt8 //@ diag(`no value of type int8 is less than math.MinInt8`)\n\t_ = i8 < math.MinInt8  //@ diag(`no value of type int8 is less than math.MinInt8`)\n\t_ = i8 >= math.MinInt8 //@ diag(`every value of type int8 is >= math.MinInt8`)\n\n\t_ = i8 > -128\n\t_ = i8 >= -128 //@ diag(`every value of type int8 is >= math.MinInt8`)\n\t_ = i8 < 127\n\t_ = i8 <= 127 //@ diag(`every value of type int8 is <= math.MaxInt8`)\n\t_ = i8 > 127  //@ diag(`no value of type int8 is greater than math.MaxInt8`)\n\t_ = i8 >= 127 //@ diag(`no value of type int8 is greater than math.MaxInt8`)\n\n\t_ = u8 < 255\n\t_ = u8 <= 255 //@ diag(`every value of type uint8 is <= math.MaxUint8`)\n\t_ = u8 > 255  //@ diag(`no value of type uint8 is greater than math.MaxUint8`)\n\t_ = u8 >= 255 //@ diag(`no value of type uint8 is greater than math.MaxUint8`)\n\n\tconst k = 255\n\t_ = u8 <= k\n}\n"
  },
  {
    "path": "staticcheck/sa4003/testdata/go1.0/CheckExtremeComparison/CheckExtremeComparison64.go",
    "content": "//go:build amd64 || arm64 || ppc64 || ppc64le || mips64 || mips64le || mips64p32 || mips64p32le || sparc64\n// +build amd64 arm64 ppc64 ppc64le mips64 mips64le mips64p32 mips64p32le sparc64\n\npackage pkg\n\nimport \"math\"\n\nfunc fn2() {\n\tvar (\n\t\tu uint\n\t\ti int\n\t)\n\t_ = u > math.MaxUint64 //@ diag(`no value of type uint is greater than math.MaxUint64`)\n\t_ = i > math.MaxInt64  //@ diag(`no value of type int is greater than math.MaxInt64`)\n\n}\n"
  },
  {
    "path": "staticcheck/sa4004/sa4004.go",
    "content": "package sa4004\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4004\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `The loop exits unconditionally after one iteration`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// This check detects some, but not all unconditional loop exits.\n\t// We give up in the following cases:\n\t//\n\t// - a goto anywhere in the loop. The goto might skip over our\n\t// return, and we don't check that it doesn't.\n\t//\n\t// - any nested, unlabelled continue, even if it is in another\n\t// loop or closure.\n\tfn := func(node ast.Node) {\n\t\tvar body *ast.BlockStmt\n\t\tswitch fn := node.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\tbody = fn.Body\n\t\tcase *ast.FuncLit:\n\t\t\tbody = fn.Body\n\t\tdefault:\n\t\t\tlint.ExhaustiveTypeSwitch(node)\n\t\t}\n\t\tif body == nil {\n\t\t\treturn\n\t\t}\n\t\tlabels := map[types.Object]ast.Stmt{}\n\t\tast.Inspect(body, func(node ast.Node) bool {\n\t\t\tlabel, ok := node.(*ast.LabeledStmt)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tlabels[pass.TypesInfo.ObjectOf(label.Label)] = label.Stmt\n\t\t\treturn true\n\t\t})\n\n\t\tast.Inspect(body, func(node ast.Node) bool {\n\t\t\tvar loop ast.Node\n\t\t\tvar body *ast.BlockStmt\n\t\t\tswitch node := node.(type) {\n\t\t\tcase *ast.ForStmt:\n\t\t\t\tbody = node.Body\n\t\t\t\tloop = node\n\t\t\tcase *ast.RangeStmt:\n\t\t\t\tok := typeutil.All(pass.TypesInfo.TypeOf(node.X), func(term *types.Term) bool {\n\t\t\t\t\tswitch term.Type().Underlying().(type) {\n\t\t\t\t\tcase *types.Slice, *types.Chan, *types.Basic, *types.Pointer, *types.Array:\n\t\t\t\t\t\treturn true\n\t\t\t\t\tcase *types.Map:\n\t\t\t\t\t\t// looping once over a map is a valid pattern for\n\t\t\t\t\t\t// getting an arbitrary element.\n\t\t\t\t\t\treturn false\n\t\t\t\t\tcase *types.Signature:\n\t\t\t\t\t\t// we have no idea what semantics the function implements\n\t\t\t\t\t\treturn false\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tlint.ExhaustiveTypeSwitch(term.Type().Underlying())\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tif !ok {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tbody = node.Body\n\t\t\t\tloop = node\n\t\t\tdefault:\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif len(body.List) < 2 {\n\t\t\t\t// TODO(dh): is this check needed? when body.List < 2,\n\t\t\t\t// then we can't find both an unconditional exit and a\n\t\t\t\t// branching statement (if, ...). and we don't flag\n\t\t\t\t// unconditional exits if there has been no branching\n\t\t\t\t// in the loop body.\n\n\t\t\t\t// avoid flagging the somewhat common pattern of using\n\t\t\t\t// a range loop to get the first element in a slice,\n\t\t\t\t// or the first rune in a string.\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tvar unconditionalExit ast.Node\n\t\t\thasBranching := false\n\t\t\tfor _, stmt := range body.List {\n\t\t\t\tswitch stmt := stmt.(type) {\n\t\t\t\tcase *ast.BranchStmt:\n\t\t\t\t\tswitch stmt.Tok {\n\t\t\t\t\tcase token.BREAK:\n\t\t\t\t\t\tif stmt.Label == nil || labels[pass.TypesInfo.ObjectOf(stmt.Label)] == loop {\n\t\t\t\t\t\t\tunconditionalExit = stmt\n\t\t\t\t\t\t}\n\t\t\t\t\tcase token.CONTINUE:\n\t\t\t\t\t\tif stmt.Label == nil || labels[pass.TypesInfo.ObjectOf(stmt.Label)] == loop {\n\t\t\t\t\t\t\tunconditionalExit = nil\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase *ast.ReturnStmt:\n\t\t\t\t\tunconditionalExit = stmt\n\t\t\t\tcase *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt:\n\t\t\t\t\thasBranching = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tif unconditionalExit == nil || !hasBranching {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tast.Inspect(body, func(node ast.Node) bool {\n\t\t\t\tif branch, ok := node.(*ast.BranchStmt); ok {\n\n\t\t\t\t\tswitch branch.Tok {\n\t\t\t\t\tcase token.GOTO:\n\t\t\t\t\t\tunconditionalExit = nil\n\t\t\t\t\t\treturn false\n\t\t\t\t\tcase token.CONTINUE:\n\t\t\t\t\t\tif branch.Label != nil && labels[pass.TypesInfo.ObjectOf(branch.Label)] != loop {\n\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t}\n\t\t\t\t\t\tunconditionalExit = nil\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t\tif unconditionalExit != nil {\n\t\t\t\treport.Report(pass, unconditionalExit, \"the surrounding loop is unconditionally terminated\")\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}\n\tcode.Preorder(pass, fn, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4004/sa4004_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4004\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4004/testdata/go1.0/CheckIneffectiveLoop/CheckIneffectiveLoop.go",
    "content": "package pkg\n\nfunc fn() {\n\tfor {\n\t\tif true {\n\t\t\tprintln()\n\t\t}\n\t\tbreak //@ diag(`the surrounding loop is unconditionally terminated`)\n\t}\n\tfor {\n\t\tif true {\n\t\t\tbreak\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\tfor range ([]int)(nil) {\n\t\tif true {\n\t\t\tprintln()\n\t\t}\n\t\tbreak //@ diag(`the surrounding loop is unconditionally terminated`)\n\t}\n\tfor range (map[int]int)(nil) {\n\t\tif true {\n\t\t\tprintln()\n\t\t}\n\t\tbreak\n\t}\n\tfor {\n\t\tif true {\n\t\t\tgoto Label\n\t\t}\n\t\tbreak\n\tLabel:\n\t}\n\tfor {\n\t\tif true {\n\t\t\tcontinue\n\t\t}\n\t\tbreak\n\t}\n\tfor {\n\t\tif true {\n\t\t\tcontinue\n\t\t}\n\t\tbreak\n\t}\n}\n\nvar z = func() {\n\tfor {\n\t\tif true {\n\t\t\tprintln()\n\t\t}\n\t\tbreak //@ diag(`the surrounding loop is unconditionally terminated`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4004/testdata/go1.18/CheckIneffectiveLoop/CheckIneffectiveLoop_generics.go",
    "content": "package pkg\n\nfunc _[T map[int]int]() {\n\tfor range (T)(nil) {\n\t\tif true {\n\t\t\tprintln()\n\t\t}\n\t\tbreak\n\t}\n}\n\nfunc _[K comparable, V any, M ~map[K]V]() {\n\tfor range (M)(nil) {\n\t\tif true {\n\t\t\tprintln()\n\t\t}\n\t\tbreak\n\t}\n}\n\nfunc _[T []int]() {\n\tfor range (T)(nil) {\n\t\tif true {\n\t\t\tprintln()\n\t\t}\n\t\tbreak //@ diag(`the surrounding loop is unconditionally terminated`)\n\t}\n}\n\nfunc _[T any, S ~[]T](x S) {\n\tfor range x {\n\t\tif true {\n\t\t\tprintln()\n\t\t}\n\t\tbreak //@ diag(`the surrounding loop is unconditionally terminated`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4005/sa4005.go",
    "content": "package sa4005\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4005\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Field assignment that will never be observed. Did you mean to use a pointer receiver?`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// The analysis only considers the receiver and its first level\n\t// fields. It doesn't look at other parameters, nor at nested\n\t// fields.\n\t//\n\t// The analysis does not detect all kinds of dead stores, only\n\t// those of fields that are never read after the write. That is,\n\t// we do not flag 'a.x = 1; a.x = 2; _ = a.x'. We might explore\n\t// this again if we add support for SROA to go/ir and implement\n\t// https://github.com/dominikh/go-tools/issues/191.\n\n\tirpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR)\nfnLoop:\n\tfor _, fn := range irpkg.SrcFuncs {\n\t\tif recv := fn.Signature.Recv(); recv == nil {\n\t\t\tcontinue\n\t\t} else if _, ok := recv.Type().Underlying().(*types.Struct); !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\trecv := fn.Params[0]\n\t\trefs := irutil.FilterDebug(*recv.Referrers())\n\t\tif len(refs) != 1 {\n\t\t\tcontinue\n\t\t}\n\t\tstore, ok := refs[0].(*ir.Store)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\talloc, ok := store.Addr.(*ir.Alloc)\n\t\tif !ok || alloc.Heap {\n\t\t\tcontinue\n\t\t}\n\n\t\treads := map[int][]ir.Instruction{}\n\t\twrites := map[int][]ir.Instruction{}\n\t\tfor _, ref := range *alloc.Referrers() {\n\t\t\tswitch ref := ref.(type) {\n\t\t\tcase *ir.FieldAddr:\n\t\t\t\tfor _, refref := range *ref.Referrers() {\n\t\t\t\t\tswitch refref.(type) {\n\t\t\t\t\tcase *ir.Store:\n\t\t\t\t\t\twrites[ref.Field] = append(writes[ref.Field], refref)\n\t\t\t\t\tcase *ir.Load:\n\t\t\t\t\t\treads[ref.Field] = append(reads[ref.Field], refref)\n\t\t\t\t\tcase *ir.DebugRef:\n\t\t\t\t\t\tcontinue\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t// this should be safe… if the field address\n\t\t\t\t\t\t// escapes, then alloc.Heap will be true.\n\t\t\t\t\t\t// there should be no instructions left that,\n\t\t\t\t\t\t// given this FieldAddr, without escaping, can\n\t\t\t\t\t\t// effect a load or store.\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase *ir.Store:\n\t\t\t\t// we could treat this as a store to every field, but\n\t\t\t\t// we don't want to decide the semantics of partial\n\t\t\t\t// struct initializers. should `v = t{x: 1}` also mark\n\t\t\t\t// v.y as being written to?\n\t\t\t\tif ref != store {\n\t\t\t\t\tcontinue fnLoop\n\t\t\t\t}\n\t\t\tcase *ir.Load:\n\t\t\t\t// a load of the entire struct loads every field\n\t\t\t\tfor i := 0; i < recv.Type().Underlying().(*types.Struct).NumFields(); i++ {\n\t\t\t\t\treads[i] = append(reads[i], ref)\n\t\t\t\t}\n\t\t\tcase *ir.DebugRef:\n\t\t\t\tcontinue\n\t\t\tdefault:\n\t\t\t\tcontinue fnLoop\n\t\t\t}\n\t\t}\n\n\t\toffset := func(instr ir.Instruction) int {\n\t\t\tfor i, other := range instr.Block().Instrs {\n\t\t\t\tif instr == other {\n\t\t\t\t\treturn i\n\t\t\t\t}\n\t\t\t}\n\t\t\tpanic(\"couldn't find instruction in its block\")\n\t\t}\n\n\t\tfor field, ws := range writes {\n\t\t\trs := reads[field]\n\t\twLoop:\n\t\t\tfor _, w := range ws {\n\t\t\t\tfor _, r := range rs {\n\t\t\t\t\tif w.Block() == r.Block() {\n\t\t\t\t\t\tif offset(r) > offset(w) {\n\t\t\t\t\t\t\t// found a reachable read of our write\n\t\t\t\t\t\t\tcontinue wLoop\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if irutil.Reachable(w.Block(), r.Block()) {\n\t\t\t\t\t\t// found a reachable read of our write\n\t\t\t\t\t\tcontinue wLoop\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfieldName := recv.Type().Underlying().(*types.Struct).Field(field).Name()\n\t\t\t\treport.Report(pass, w, fmt.Sprintf(\"ineffective assignment to field %s.%s\", recv.Type().(interface{ Obj() *types.TypeName }).Obj().Name(), fieldName))\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4005/sa4005_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4005\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4005/testdata/go1.0/CheckIneffectiveFieldAssignments/CheckIneffectiveFieldAssignments.go",
    "content": "package pkg\n\ntype T1 struct {\n\tT2 // make sure embedded fields don't throw off our numbering\n\tx  int\n\ty  int\n\tz  T2\n}\n\ntype T2 struct {\n\tx int\n\ty int\n\tz int\n}\n\ntype T3 struct {\n\tT2\n}\n\nfunc (v T1) fn1() {\n\tv.x = 1\n\tv.y = 1 //@ diag(`ineffective assignment to field T1.y`)\n\tprintln(v.x)\n}\n\nfunc (v T1) fn2() {\n\tprintln(v.x)\n\tv.x = 1 //@ diag(`ineffective assignment to field T1.x`)\n}\n\nfunc (v T1) fn3() {\n\tif true {\n\t\tprintln(v.x)\n\t}\n\tv.x = 1 //@ diag(`ineffective assignment to field T1.x`)\n}\n\nfunc (v T1) fn10() {\n\tv.x = 1\n\tif true {\n\t\tprintln(v.x)\n\t}\n}\n\nfunc (v T1) fn4() {\n\tv.x = 1\n\tv.dump()\n}\n\nfunc (v T1) fn5() {\n\tv.dump()\n\t// This is currently broken because of our more aggressive lifting\n\tv.x = 1\n}\n\nfunc (v T1) fn6() {\n\tv.x = 1\n\tv.y = 1\n\tprintln(v.y)\n\tprintln(v.x)\n}\n\nfunc (v T1) fn7() {\n\t// not currently being flagged because it's a nested field\n\tv.z.x = 1\n}\n\nfunc (v T1) fn8() {\n\tv.x++ //@ diag(`ineffective assignment to field T1.x`)\n}\n\nfunc (v T1) fn9() {\n\tv.x++\n\tprintln(v.x)\n}\n\nfunc (v T1) fn11() {\n\tv = T1{x: 42, y: 23} // not currently being flagged\n}\n\nfunc (v T1) fn12() {\n\tv = T1{x: 42, y: 23} // not currently being flagged\n\tprintln(v.y)\n}\n\nfunc (v T1) fn13() {\n\tv = T1{x: 42}\n\tv.y = 23 // not currently being flagged, we gave up when we saw the assignment to v\n\tprintln(v.x)\n}\n\nfunc (v T1) fn14() {\n\tv = T1{x: 42} // not currently being flagged\n\tv.y = 23\n\tprintln(v.y)\n}\n\nfunc (v T1) fn15() {\n\t// not currently being flagged\n\tv = T1{x: 42}\n}\n\nfunc (v T1) dump() {}\n\nfunc (v T3) fn1() {\n\t// not currently being flagged because it's a nested field (via\n\t// embedding)\n\tv.x = 1\n}\n"
  },
  {
    "path": "staticcheck/sa4005/testdata/go1.0/CheckIneffectiveFieldAssignments/issue141.go",
    "content": "package pkg\n\nimport \"fmt\"\n\n// T is t\ntype T struct {\n\tX bool\n\tF string\n}\n\n// Modify modifies T.F to say modified, then calls EchoF.\nfunc (t T) Modify() {\n\tif t.X {\n\t\tt.X, t.F = true, \"modified\"\n\t}\n\tt.EchoF()\n}\n\n// EchoF prints F.\nfunc (t T) EchoF() {\n\tfmt.Println(t.F)\n}\n\nfunc main() {\n\tt := T{X: true, F: \"original\"}\n\n\tt.EchoF()  // output: original\n\tt.Modify() // output: modified\n\tt.EchoF()  // output: original\n}\n"
  },
  {
    "path": "staticcheck/sa4006/sa4006.go",
    "content": "package sa4006\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4006\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tif irutil.IsExample(fn) {\n\t\t\tcontinue\n\t\t}\n\t\tnode := fn.Source()\n\t\tif node == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif gen, ok := code.Generator(pass, node.Pos()); ok && gen == generated.Goyacc {\n\t\t\t// Don't flag unused values in code generated by goyacc.\n\t\t\t// There may be hundreds of those due to the way the state\n\t\t\t// machine is constructed.\n\t\t\tcontinue\n\t\t}\n\n\t\tswitchTags := map[ir.Value]struct{}{}\n\t\tast.Inspect(node, func(node ast.Node) bool {\n\t\t\ts, ok := node.(*ast.SwitchStmt)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tv, _ := fn.ValueForExpr(s.Tag)\n\t\t\tswitchTags[v] = struct{}{}\n\t\t\treturn true\n\t\t})\n\n\t\t// OPT(dh): don't use a map, possibly use a bitset\n\t\tvar hasUse func(v ir.Value, seen map[ir.Value]struct{}) bool\n\t\thasUse = func(v ir.Value, seen map[ir.Value]struct{}) bool {\n\t\t\tif _, ok := seen[v]; ok {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif _, ok := switchTags[v]; ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\trefs := v.Referrers()\n\t\t\tif refs == nil {\n\t\t\t\t// TODO investigate why refs can be nil\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tfor _, ref := range *refs {\n\t\t\t\tswitch ref := ref.(type) {\n\t\t\t\tcase *ir.DebugRef:\n\t\t\t\tcase *ir.Sigma:\n\t\t\t\t\tif seen == nil {\n\t\t\t\t\t\tseen = map[ir.Value]struct{}{}\n\t\t\t\t\t}\n\t\t\t\t\tseen[v] = struct{}{}\n\t\t\t\t\tif hasUse(ref, seen) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\tcase *ir.Phi:\n\t\t\t\t\tif seen == nil {\n\t\t\t\t\t\tseen = map[ir.Value]struct{}{}\n\t\t\t\t\t}\n\t\t\t\t\tseen[v] = struct{}{}\n\t\t\t\t\tif hasUse(ref, seen) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\n\t\tast.Inspect(node, func(node ast.Node) bool {\n\t\t\tinc, ok := node.(*ast.IncDecStmt)\n\t\t\tif ok {\n\t\t\t\tval, _ := fn.ValueForExpr(inc.X)\n\t\t\t\tif val == nil {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif _, ok := val.(*ir.Const); ok {\n\t\t\t\t\t// a zero-valued constant, for example in 'foo := []string(nil)'\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif !hasUse(val, nil) {\n\t\t\t\t\treport.Report(pass, inc, fmt.Sprintf(\"this value of %s is never used\", inc.X))\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tassign, ok := node.(*ast.AssignStmt)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif len(assign.Lhs) > 1 && len(assign.Rhs) == 1 {\n\t\t\t\t// Either a function call with multiple return values,\n\t\t\t\t// or a comma-ok assignment\n\n\t\t\t\tval, _ := fn.ValueForExpr(assign.Rhs[0])\n\t\t\t\tif val == nil {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\trefs := val.Referrers()\n\t\t\t\tif refs == nil {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tfor _, ref := range *refs {\n\t\t\t\t\tex, ok := ref.(*ir.Extract)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif !hasUse(ex, nil) {\n\t\t\t\t\t\tlhs := assign.Lhs[ex.Index]\n\t\t\t\t\t\tif ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == \"_\" {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\treport.Report(pass, assign, fmt.Sprintf(\"this value of %s is never used\", lhs))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tfor i, lhs := range assign.Lhs {\n\t\t\t\trhs := assign.Rhs[i]\n\t\t\t\tif ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == \"_\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tval, _ := fn.ValueForExpr(rhs)\n\t\t\t\tif val == nil {\n\t\t\t\t\tif assign.Tok != token.ASSIGN { // +=, *=, etc.\n\t\t\t\t\t\tval, _ = fn.ValueForExpr(lhs)\n\t\t\t\t\t}\n\t\t\t\t\tif val == nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif _, ok := val.(*ir.Const); ok {\n\t\t\t\t\t// a zero-valued constant, for example in 'foo := []string(nil)'\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !hasUse(val, nil) {\n\t\t\t\t\treport.Report(pass, assign, fmt.Sprintf(\"this value of %s is never used\", lhs))\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4006/sa4006_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4006\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4006/testdata/go1.0/CheckUnreadVariableValues/CheckUnreadVariableValues.go",
    "content": "package pkg\n\nimport \"fmt\"\n\nfunc fn1() {\n\tvar x int\n\tx = gen() //@ diag(`this value of x is never used`)\n\tx = gen()\n\tprintln(x)\n\n\tvar y int\n\tif true {\n\t\ty = gen() //@ diag(`this value of y is never used`)\n\t}\n\ty = gen()\n\tprintln(y)\n}\n\nfunc gen() int {\n\tprintln() // make it unpure\n\treturn 0\n}\n\nfunc fn2() {\n\tx, y := gen(), gen() //@ diag(`this value of x is never used`), diag(`this value of y is never used`)\n\tx, y = gen(), gen()\n\tprintln(x, y)\n}\n\nfunc fn3() {\n\tx := uint32(0)\n\tif true {\n\t\tx = 1\n\t} else {\n\t\tx = 2\n\t}\n\tprintln(x)\n}\n\nfunc gen2() (int, int) {\n\tprintln()\n\treturn 0, 0\n}\n\nfunc fn4() {\n\tx, y := gen2() //@ diag(`this value of x is never used`)\n\tprintln(y)\n\tx, y = gen2() //@ diag(`this value of x is never used`), diag(`this value of y is never used`)\n\tx, _ = gen2() //@ diag(`this value of x is never used`)\n\tx, y = gen2()\n\tprintln(x, y)\n}\n\nfunc fn5(m map[string]string) {\n\tv, ok := m[\"\"] //@ diag(`this value of v is never used`), diag(`this value of ok is never used`)\n\tv, ok = m[\"\"]\n\tprintln(v, ok)\n}\n\nfunc fn6() {\n\tx := gen()\n\t// Do not report variables if they've been assigned to the blank identifier\n\t_ = x\n}\n\nfunc fn7() {\n\tfunc() {\n\t\tvar x int\n\t\tx = gen() //@ diag(`this value of x is never used`)\n\t\tx = gen()\n\t\tprintln(x)\n\t}()\n}\n\nfunc fn() int { println(); return 0 }\n\nvar y = func() {\n\tv := fn() //@ diag(`never used`)\n\tv = fn()\n\tprintln(v)\n}\n\nfunc fn8() {\n\tx := gen()\n\tswitch x {\n\t}\n\n\ty := gen() //@ diag(`this value of y is never used`)\n\ty = gen()\n\tswitch y {\n\t}\n\n\tz, _ := gen2()\n\tswitch z {\n\t}\n\n\t_, a := gen2()\n\tswitch a {\n\t}\n\n\tb, c := gen2() //@ diag(`this value of b is never used`)\n\tprintln(c)\n\tb, c = gen2() //@ diag(`this value of c is never used`)\n\tswitch b {\n\t}\n}\n\nfunc fn9() {\n\txs := []int{}\n\tfor _, x := range xs {\n\t\tfoo, err := work(x) //@ diag(`this value of foo is never used`)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif !foo {\n\t\t\tcontinue\n\t\t}\n\t}\n}\n\nfunc work(int) (bool, error) { return false, nil }\n\nfunc resolveWeakTypes(types []int) {\n\tfor i := range types {\n\t\trunEnd := findRunLimit(i)\n\n\t\tif true {\n\t\t\t_ = runEnd\n\t\t}\n\t\ti = runEnd //@ diag(`this value of i is never used`)\n\t}\n}\n\nfunc findRunLimit(int) int { return 0 }\n\nfunc fn10() {\n\tslice := []string(nil)\n\tif true {\n\t\tslice = []string{\"1\", \"2\"}\n\t} else {\n\t\tslice = []string{\"3\", \"4\"}\n\t}\n\tfmt.Println(slice)\n}\n\nfunc issue1329() {\n\t{\n\t\tn := 1\n\t\tn += 1 //@ diag(`this value of n is never used`)\n\t}\n\t{\n\t\tn := 1\n\t\tn ^= 1 //@ diag(`this value of n is never used`)\n\t}\n\t{\n\t\tn := \"\"\n\t\tn += \"\" //@ diag(`this value of n is never used`)\n\t}\n\t{\n\t\tn := 1\n\t\tn++ //@ diag(`this value of n is never used`)\n\t}\n\n\t{\n\t\tn := 1\n\t\tn += 1\n\t\tfmt.Println(n)\n\t}\n\t{\n\t\tn := 1\n\t\tn ^= 1\n\t\tfmt.Println(n)\n\t}\n\t{\n\t\tn := \"\"\n\t\tn += \"\"\n\t\tfmt.Println(n)\n\t}\n\t{\n\t\tn := 1\n\t\tn++\n\t\tfmt.Println(n)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4006/testdata/go1.0/CheckUnreadVariableValues/CheckUnreadVariableValues_test.go",
    "content": "package pkg\n\nimport \"testing\"\n\nfunc TestFoo(t *testing.T) {\n\tx := fn() //@ diag(`never used`)\n\tx = fn()\n\tprintln(x)\n}\n\nfunc ExampleFoo() {\n\tx := fn()\n\tx = fn()\n\tprintln(x)\n}\n"
  },
  {
    "path": "staticcheck/sa4008/sa4008.go",
    "content": "package sa4008\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4008\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `The variable in the loop condition never changes, are you incrementing the wrong variable?`,\n\t\tText: `For example:\n\n\tfor i := 0; i < 10; j++ { ... }\n\nThis may also occur when a loop can only execute once because of unconditional\ncontrol flow that terminates the loop. For example, when a loop body contains an\nunconditional break, return, or panic:\n\n\tfunc f() {\n\t\tpanic(\"oops\")\n\t}\n\tfunc g() {\n\t\tfor i := 0; i < 10; i++ {\n\t\t\t// f unconditionally calls panic, which means \"i\" is\n\t\t\t// never incremented.\n\t\t\tf()\n\t\t}\n\t}`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tcb := func(node ast.Node) bool {\n\t\t\tloop, ok := node.(*ast.ForStmt)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif loop.Init == nil || loop.Cond == nil || loop.Post == nil {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tinit, ok := loop.Init.(*ast.AssignStmt)\n\t\t\tif !ok || len(init.Lhs) != 1 || len(init.Rhs) != 1 {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tcond, ok := loop.Cond.(*ast.BinaryExpr)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tx, ok := cond.X.(*ast.Ident)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tlhs, ok := init.Lhs[0].(*ast.Ident)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif pass.TypesInfo.ObjectOf(x) != pass.TypesInfo.ObjectOf(lhs) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif _, ok := loop.Post.(*ast.IncDecStmt); !ok {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tv, isAddr := fn.ValueForExpr(cond.X)\n\t\t\tif v == nil || isAddr {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tswitch v := v.(type) {\n\t\t\tcase *ir.Phi:\n\t\t\t\tops := v.Operands(nil)\n\t\t\t\tif len(ops) != 2 {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\t_, ok := (*ops[0]).(*ir.Const)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tsigma, ok := (*ops[1]).(*ir.Sigma)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif sigma.X != v {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\tcase *ir.Load:\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treport.Report(pass, cond, \"variable in loop condition never changes\")\n\n\t\t\treturn true\n\t\t}\n\t\tif source := fn.Source(); source != nil {\n\t\t\tast.Inspect(source, cb)\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4008/sa4008_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4008\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4008/testdata/go1.0/CheckLoopCondition/CheckLoopCondition.go",
    "content": "package pkg\n\nfunc fn() {\n\tfor i := 0; i < 10; i++ {\n\t\tfor j := 0; j < 10; i++ { //@ diag(`variable in loop condition never changes`)\n\t\t}\n\t}\n\n\tcounter := 0\n\tfor i := 0; i < 10; i++ {\n\t\tfor j := 0; j < 10; counter++ {\n\t\t\tx := &j\n\t\t\t*x++\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4009/sa4009.go",
    "content": "package sa4009\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4009\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `A function argument is overwritten before its first use`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tcb := func(node ast.Node) bool {\n\t\t\tvar typ *ast.FuncType\n\t\t\tvar body *ast.BlockStmt\n\t\t\tswitch fn := node.(type) {\n\t\t\tcase *ast.FuncDecl:\n\t\t\t\ttyp = fn.Type\n\t\t\t\tbody = fn.Body\n\t\t\tcase *ast.FuncLit:\n\t\t\t\ttyp = fn.Type\n\t\t\t\tbody = fn.Body\n\t\t\t}\n\t\t\tif body == nil {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif len(typ.Params.List) == 0 {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tfor _, field := range typ.Params.List {\n\t\t\t\tfor _, arg := range field.Names {\n\t\t\t\t\tobj := pass.TypesInfo.ObjectOf(arg)\n\t\t\t\t\tvar irobj *ir.Parameter\n\t\t\t\t\tfor _, param := range fn.Params {\n\t\t\t\t\t\tif param.Object() == obj {\n\t\t\t\t\t\t\tirobj = param\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif irobj == nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\trefs := irobj.Referrers()\n\t\t\t\t\tif refs == nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif len(irutil.FilterDebug(*refs)) != 0 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tvar assignment ast.Node\n\t\t\t\t\tast.Inspect(body, func(node ast.Node) bool {\n\t\t\t\t\t\tif assignment != nil {\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t\tassign, ok := node.(*ast.AssignStmt)\n\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor _, lhs := range assign.Lhs {\n\t\t\t\t\t\t\tident, ok := lhs.(*ast.Ident)\n\t\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif pass.TypesInfo.ObjectOf(ident) == obj {\n\t\t\t\t\t\t\t\tassignment = assign\n\t\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true\n\t\t\t\t\t})\n\t\t\t\t\tif assignment != nil {\n\t\t\t\t\t\treport.Report(pass, arg, fmt.Sprintf(\"argument %s is overwritten before first use\", arg),\n\t\t\t\t\t\t\treport.Related(assignment, fmt.Sprintf(\"assignment to %s\", arg)))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t\tif source := fn.Source(); source != nil {\n\t\t\tast.Inspect(source, cb)\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4009/sa4009_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4009\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4009/testdata/go1.0/CheckArgOverwritten/CheckArgOverwritten.go",
    "content": "package pkg\n\nvar x = func(arg int) { //@ diag(`overwritten`)\n\targ = 1\n\tprintln(arg)\n}\n"
  },
  {
    "path": "staticcheck/sa4010/sa4010.go",
    "content": "package sa4010\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4010\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `The result of \\'append\\' will never be observed anywhere`,\n\t\tText: `Calls to \\'append\\' produce a new slice value. When the result of\n\\'append\\' is assigned to a variable that is never subsequently read, the\nappend operation may have an unintended effect.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tisAppend := func(ins ir.Value) bool {\n\t\tcall, ok := ins.(*ir.Call)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\t\tif call.Call.IsInvoke() {\n\t\t\treturn false\n\t\t}\n\t\tif builtin, ok := call.Call.Value.(*ir.Builtin); !ok || builtin.Name() != \"append\" {\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t}\n\n\t// We have to be careful about aliasing.\n\t// Multiple slices may refer to the same backing array,\n\t// making appends observable even when we don't see the result of append be used anywhere.\n\t//\n\t// We will have to restrict ourselves to slices that have been allocated within the function,\n\t// haven't been sliced,\n\t// and haven't been passed anywhere that could retain them (such as function calls or memory stores).\n\t//\n\t// We check whether an append should be flagged in two steps.\n\t//\n\t// In the first step, we look at the data flow graph, starting in reverse from the argument to append, till we reach the root.\n\t// This graph must only consist of the following instructions:\n\t//\n\t// - phi\n\t// - sigma\n\t// - slice\n\t// - const nil\n\t// - MakeSlice\n\t// - Alloc\n\t// - calls to append\n\t//\n\t// If this step succeeds, we look at all referrers of the values found in the first step, recursively.\n\t// These referrers must either be in the set of values found in the first step,\n\t// be DebugRefs,\n\t// or fulfill the same type requirements as step 1, with the exception of appends, which are forbidden.\n\t//\n\t// If both steps succeed then we know that the backing array hasn't been aliased in an observable manner.\n\t//\n\t// We could relax these restrictions by making use of additional information:\n\t// - if we passed the slice to a function that doesn't retain the slice then we can still flag it\n\t// - if a slice has been sliced but is dead afterwards, we can flag appends to the new slice\n\n\t// OPT(dh): We could cache the results of both validate functions.\n\t// However, we only use these functions on values that we otherwise want to flag, which are very few.\n\t// Not caching values hasn't increased the runtimes for the standard library nor k8s.\n\tvar validateArgument func(v ir.Value, seen map[ir.Value]struct{}) bool\n\tvalidateArgument = func(v ir.Value, seen map[ir.Value]struct{}) bool {\n\t\tif _, ok := seen[v]; ok {\n\t\t\t// break cycle\n\t\t\treturn true\n\t\t}\n\t\tseen[v] = struct{}{}\n\t\tswitch v := v.(type) {\n\t\tcase *ir.Phi:\n\t\t\tfor _, edge := range v.Edges {\n\t\t\t\tif !validateArgument(edge, seen) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\tcase *ir.Sigma:\n\t\t\treturn validateArgument(v.X, seen)\n\t\tcase *ir.Slice:\n\t\t\treturn validateArgument(v.X, seen)\n\t\tcase *ir.Const:\n\t\t\treturn true\n\t\tcase *ir.MakeSlice:\n\t\t\treturn true\n\t\tcase *ir.Alloc:\n\t\t\treturn true\n\t\tcase *ir.Call:\n\t\t\tif isAppend(v) {\n\t\t\t\treturn validateArgument(v.Call.Args[0], seen)\n\t\t\t}\n\t\t\treturn false\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\t}\n\n\tvar validateReferrers func(v ir.Value, seen map[ir.Instruction]struct{}) bool\n\tvalidateReferrers = func(v ir.Value, seen map[ir.Instruction]struct{}) bool {\n\t\tfor _, ref := range *v.Referrers() {\n\t\t\tif _, ok := seen[ref]; ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tseen[ref] = struct{}{}\n\t\t\tswitch ref.(type) {\n\t\t\tcase *ir.Phi:\n\t\t\tcase *ir.Sigma:\n\t\t\tcase *ir.Slice:\n\t\t\tcase *ir.Const:\n\t\t\tcase *ir.MakeSlice:\n\t\t\tcase *ir.Alloc:\n\t\t\tcase *ir.DebugRef:\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif ref, ok := ref.(ir.Value); ok {\n\t\t\t\tif !validateReferrers(ref, seen) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tfor _, block := range fn.Blocks {\n\t\t\tfor _, ins := range block.Instrs {\n\t\t\t\tval, ok := ins.(ir.Value)\n\t\t\t\tif !ok || !isAppend(val) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tisUsed := false\n\t\t\t\tvisited := map[ir.Instruction]bool{}\n\t\t\t\tvar walkRefs func(refs []ir.Instruction)\n\t\t\t\twalkRefs = func(refs []ir.Instruction) {\n\t\t\t\tloop:\n\t\t\t\t\tfor _, ref := range refs {\n\t\t\t\t\t\tif visited[ref] {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvisited[ref] = true\n\t\t\t\t\t\tif _, ok := ref.(*ir.DebugRef); ok {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tswitch ref := ref.(type) {\n\t\t\t\t\t\tcase *ir.Phi:\n\t\t\t\t\t\t\twalkRefs(*ref.Referrers())\n\t\t\t\t\t\tcase *ir.Sigma:\n\t\t\t\t\t\t\twalkRefs(*ref.Referrers())\n\t\t\t\t\t\tcase ir.Value:\n\t\t\t\t\t\t\tif !isAppend(ref) {\n\t\t\t\t\t\t\t\tisUsed = true\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\twalkRefs(*ref.Referrers())\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\tcase ir.Instruction:\n\t\t\t\t\t\t\tisUsed = true\n\t\t\t\t\t\t\tbreak loop\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\trefs := val.Referrers()\n\t\t\t\tif refs == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\twalkRefs(*refs)\n\n\t\t\t\tif isUsed {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tseen := map[ir.Value]struct{}{}\n\t\t\t\tif !validateArgument(ins.(*ir.Call).Call.Args[0], seen) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tseen2 := map[ir.Instruction]struct{}{}\n\t\t\t\tfor k := range seen {\n\t\t\t\t\t// the only values we allow are also instructions, so this type assertion cannot fail\n\t\t\t\t\tseen2[k.(ir.Instruction)] = struct{}{}\n\t\t\t\t}\n\t\t\t\tseen2[ins] = struct{}{}\n\t\t\t\tfailed := false\n\t\t\t\tfor v := range seen {\n\t\t\t\t\tif !validateReferrers(v, seen2) {\n\t\t\t\t\t\tfailed = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !failed {\n\t\t\t\t\treport.Report(pass, ins, \"this result of append is never used, except maybe in other appends\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4010/sa4010_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4010\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4010/testdata/go1.0/CheckIneffectiveAppend/CheckIneffectiveAppend.go",
    "content": "package pkg\n\nimport \"fmt\"\n\nfunc fn1() {\n\tvar s []int\n\ts = append(s, 1) //@ diag(`this result of append is never used`)\n\ts = append(s, 1) //@ diag(`this result of append is never used`)\n}\n\nfunc fn2() (named []int) {\n\tnamed = append(named, 1)\n\treturn\n}\n\nfunc fn3() {\n\ts := make([]int, 0)\n\ts = append(s, 1) //@ diag(`this result of append is never used`)\n}\n\nfunc fn3_1(n int) {\n\ts := make([]int, n)\n\ts = append(s, 1) //@ diag(`this result of append is never used`)\n}\n\nfunc fn4() []int {\n\tvar s []int\n\ts = append(s, 1)\n\treturn s\n}\n\nfunc fn5() {\n\tvar s []int\n\ts = append(s, 1)\n\tfn6(s)\n}\n\nfunc fn6([]int) {}\n\nfunc fn7() {\n\tvar s []int\n\tfn8(&s)\n\ts = append(s, 1)\n}\n\nfunc fn8(*[]int) {}\n\nfunc fn9() {\n\tvar s []int\n\ts = append(s, 1)\n\tfmt.Println(s)\n\ts = append(s, 1)\n}\n\nfunc fn10() {\n\tvar s []int\n\treturn\n\ts = append(s, 1)\n}\n\nfunc fn11() {\n\tvar s []int\n\tfor x := 0; x < 10; x++ {\n\t\ts = append(s, 1) //@ diag(`this result of append is never used`)\n\t}\n}\n\nfunc fn12(a []int) {\n\tb := a[:0]\n\tfor _, x := range a {\n\t\tif true {\n\t\t\tb = append(b, x)\n\t\t}\n\t}\n}\n\nfunc fn13() []byte {\n\ta := make([]byte, 10)\n\tb := a[:5]\n\tfor i := 0; i < 2; i++ {\n\t\ta = append(a, 1)\n\t}\n\treturn b\n}\n\nfunc fn14() []byte {\n\ta := make([]byte, 10)\n\tb := a[:5]\n\tfor i := 0; i < 2; i++ {\n\t\tb = append(b, 1)\n\t}\n\treturn a\n}\n\nfunc fn15() {\n\ts := make([]byte, 0, 1)\n\tretain(s)\n\ts = append(s, 1)\n}\n\nfunc fn16(s []byte) {\n\tfor i := 0; i < 2; i++ {\n\t\ts = append(s, 1)\n\t}\n}\n\nfunc fn17(x *[5]byte) {\n\ts := x[:0]\n\tfor i := 0; i < 2; i++ {\n\t\ts = append(s, 1)\n\t}\n}\n\nfunc fn18() {\n\tvar x [4]byte\n\ts := x[:0]\n\tfor i := 0; i < 2; i++ {\n\t\ts = append(s, 1)\n\t}\n\t_ = x\n}\n\nfunc fn19() [4]int {\n\tvar x [4]int\n\ts := x[:]\n\ts = append(s, 1)\n\treturn x\n}\n\nfunc fn20() {\n\tvar x [4]int\n\ts := x[:]\n\ts = append(s, 1) //@ diag(`this result of append is never used`)\n}\n\nfunc fn21() {\n\tvar x []byte\n\tx = append(x, 1)\n\tretain(x)\n\tx = append(x, 2)\n}\n\nfunc fn22() {\n\t// we should probably flag this, but we currently don't\n\tvar x1 []byte\n\tx2 := append(x1, 1)\n\tx2 = append(x2, 2)\n\tx3 := append(x1, 3)\n\tx3 = append(x3, 4)\n}\n\nfunc fn23(n int) []int {\n\ts := make([]int, 0, n)\n\ts2 := append(s, 1, 2, 3) // this can be observed by extending the capacity of x\n\ts2 = append(s2, 4)\n\tx := append(s, 2)\n\treturn x\n}\n\nfunc fn24() []byte {\n\tx := make([]byte, 0, 24)\n\ts1 := append(x, 1)\n\ts2 := append(s1, 2)\n\ts2 = append(s2, 3)\n\ts3 := append(s1, 4)\n\treturn s3\n}\n\nfunc fn25() {\n\tvar s []byte\n\tif true {\n\t\ts = append(s, 1)\n\t}\n\ts = append(s, 2) //@ diag(`this result of append is never used`)\n}\n\nfunc fn26() {\n\tvar s []byte\n\tif true {\n\t\ts = append(s, 1)\n\t\tretain(s)\n\t}\n\ts = append(s, 2)\n}\n\nfunc fn27() {\n\tvar s []byte\n\tif true {\n\t\ts = make([]byte, 0, 1)\n\t} else {\n\t\ts = make([]byte, 0, 2)\n\t}\n\ts = append(s, 1) //@ diag(`this result of append is never used`)\n}\n\nfunc fn28() {\n\tvar s []byte\n\tif true {\n\t\ts = make([]byte, 0, 1)\n\t} else {\n\t\ts = make([]byte, 0, 2)\n\t\tretain(s)\n\t}\n\ts = append(s, 1)\n}\n\nfunc fn29() {\n\tx := gen()\n\tx = append(x, 1)\n}\n\nfunc fn30(x T) {\n\ts := x.s\n\ts = append(s, 1)\n}\n\nvar Global []int\n\nfunc fn31() {\n\tGlobal = append(Global, 1)\n}\n\ntype T struct {\n\ts []byte\n}\n\nfunc gen() []byte { return nil }\n\nfunc retain([]byte) {}\n"
  },
  {
    "path": "staticcheck/sa4011/sa4011.go",
    "content": "package sa4011\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4011\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Break statement with no effect. Did you mean to break out of an outer loop?`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tvar body *ast.BlockStmt\n\t\tswitch node := node.(type) {\n\t\tcase *ast.ForStmt:\n\t\t\tbody = node.Body\n\t\tcase *ast.RangeStmt:\n\t\t\tbody = node.Body\n\t\tdefault:\n\t\t\tlint.ExhaustiveTypeSwitch(node)\n\t\t}\n\t\tfor _, stmt := range body.List {\n\t\t\tvar blocks [][]ast.Stmt\n\t\t\tswitch stmt := stmt.(type) {\n\t\t\tcase *ast.SwitchStmt:\n\t\t\t\tfor _, c := range stmt.Body.List {\n\t\t\t\t\tblocks = append(blocks, c.(*ast.CaseClause).Body)\n\t\t\t\t}\n\t\t\tcase *ast.SelectStmt:\n\t\t\t\tfor _, c := range stmt.Body.List {\n\t\t\t\t\tblocks = append(blocks, c.(*ast.CommClause).Body)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfor _, body := range blocks {\n\t\t\t\tif len(body) == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tlasts := []ast.Stmt{body[len(body)-1]}\n\t\t\t\t// TODO(dh): unfold all levels of nested block\n\t\t\t\t// statements, not just a single level if statement\n\t\t\t\tif ifs, ok := lasts[0].(*ast.IfStmt); ok {\n\t\t\t\t\tif len(ifs.Body.List) == 0 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tlasts[0] = ifs.Body.List[len(ifs.Body.List)-1]\n\n\t\t\t\t\tif block, ok := ifs.Else.(*ast.BlockStmt); ok {\n\t\t\t\t\t\tif len(block.List) != 0 {\n\t\t\t\t\t\t\tlasts = append(lasts, block.List[len(block.List)-1])\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor _, last := range lasts {\n\t\t\t\t\tbranch, ok := last.(*ast.BranchStmt)\n\t\t\t\t\tif !ok || branch.Tok != token.BREAK || branch.Label != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\treport.Report(pass, branch, \"ineffective break statement. Did you mean to break out of the outer loop?\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.RangeStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4011/sa4011_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4011\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4011/testdata/go1.0/CheckScopedBreak/CheckScopedBreak.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar ch chan int\n\tfor {\n\t\tswitch {\n\t\tcase true:\n\t\t\tbreak //@ diag(`ineffective break statement`)\n\t\tdefault:\n\t\t\tbreak //@ diag(`ineffective break statement`)\n\t\t}\n\t}\n\n\tfor {\n\t\tselect {\n\t\tcase <-ch:\n\t\t\tbreak //@ diag(`ineffective break statement`)\n\t\t}\n\t}\n\n\tfor {\n\t\tswitch {\n\t\tcase true:\n\t\t}\n\n\t\tswitch {\n\t\tcase true:\n\t\t\tbreak //@ diag(`ineffective break statement`)\n\t\t}\n\n\t\tswitch {\n\t\tcase true:\n\t\t}\n\t}\n\n\tfor {\n\t\tswitch {\n\t\tcase true:\n\t\t\tif true {\n\t\t\t\tbreak //@ diag(`ineffective break statement`)\n\t\t\t} else {\n\t\t\t\tbreak //@ diag(`ineffective break statement`)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor {\n\t\tswitch {\n\t\tcase true:\n\t\t\tif true {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tprintln(\"do work\")\n\t\t}\n\t}\n\nlabel:\n\tfor {\n\t\tswitch {\n\t\tcase true:\n\t\t\tbreak label\n\t\t}\n\t}\n\n\tfor range ([]int)(nil) {\n\t\tswitch {\n\t\tdefault:\n\t\t\tbreak //@ diag(`ineffective break statement`)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4012/sa4012.go",
    "content": "package sa4012\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4012\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Comparing a value against NaN even though no value is equal to NaN`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tisNaN := func(v ir.Value) bool {\n\t\tcall, ok := v.(*ir.Call)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\t\treturn irutil.IsCallTo(call.Common(), \"math.NaN\")\n\t}\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tfor _, block := range fn.Blocks {\n\t\t\tfor _, ins := range block.Instrs {\n\t\t\t\tins, ok := ins.(*ir.BinOp)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif isNaN(irutil.Flatten(ins.X)) || isNaN(irutil.Flatten(ins.Y)) {\n\t\t\t\t\treport.Report(pass, ins, \"no value is equal to NaN, not even NaN itself\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4012/sa4012_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4012\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4012/testdata/go1.0/CheckNaNComparison/CheckNaNComparison.go",
    "content": "package pkg\n\nimport \"math\"\n\nfunc fn(f float64) {\n\t_ = f == math.NaN() //@ diag(`no value is equal to NaN`)\n\t_ = f > math.NaN()  //@ diag(`no value is equal to NaN`)\n\t_ = f != math.NaN() //@ diag(`no value is equal to NaN`)\n}\n\nfunc fn2(f float64) {\n\tx := math.NaN()\n\tif true {\n\t\tif f == x { //@ diag(`no value is equal to NaN`)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4013/sa4013.go",
    "content": "package sa4013\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4013\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Negating a boolean twice (\\'!!b\\') is the same as writing \\'b\\'. This is either redundant, or a typo.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkDoubleNegationQ = pattern.MustParse(`(UnaryExpr \"!\" single@(UnaryExpr \"!\" x))`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkDoubleNegationQ) {\n\t\treport.Report(pass, node, \"negating a boolean twice has no effect; is this a typo?\", report.Fixes(\n\t\t\tedit.Fix(\"Turn into single negation\", edit.ReplaceWithNode(pass.Fset, node, m.State[\"single\"].(ast.Node))),\n\t\t\tedit.Fix(\"Remove double negation\", edit.ReplaceWithNode(pass.Fset, node, m.State[\"x\"].(ast.Node)))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4013/sa4013_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4013\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4013/testdata/go1.0/CheckDoubleNegation/CheckDoubleNegation.go",
    "content": "package pkg\n\nfunc fn(b1, b2 bool) {\n\tif !!b1 { //@ diag(`negating a boolean twice`)\n\t\tprintln()\n\t}\n\n\tif b1 && !!b2 { //@ diag(`negating a boolean twice`)\n\t\tprintln()\n\t}\n\n\tif !(!b1) { //@ diag(`negating a boolean twice`)\n\t\tprintln()\n\t}\n\n\tif !b1 {\n\t\tprintln()\n\t}\n\n\tif !b1 && !b2 {\n\t\tprintln()\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4013/testdata/go1.0/CheckDoubleNegation/CheckDoubleNegation.go.golden",
    "content": "-- Turn into single negation --\npackage pkg\n\nfunc fn(b1, b2 bool) {\n\tif !b1 { //@ diag(`negating a boolean twice`)\n\t\tprintln()\n\t}\n\n\tif b1 && !b2 { //@ diag(`negating a boolean twice`)\n\t\tprintln()\n\t}\n\n\tif !b1 { //@ diag(`negating a boolean twice`)\n\t\tprintln()\n\t}\n\n\tif !b1 {\n\t\tprintln()\n\t}\n\n\tif !b1 && !b2 {\n\t\tprintln()\n\t}\n}\n\n-- Remove double negation --\npackage pkg\n\nfunc fn(b1, b2 bool) {\n\tif b1 { //@ diag(`negating a boolean twice`)\n\t\tprintln()\n\t}\n\n\tif b1 && b2 { //@ diag(`negating a boolean twice`)\n\t\tprintln()\n\t}\n\n\tif b1 { //@ diag(`negating a boolean twice`)\n\t\tprintln()\n\t}\n\n\tif !b1 {\n\t\tprintln()\n\t}\n\n\tif !b1 && !b2 {\n\t\tprintln()\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4014/sa4014.go",
    "content": "package sa4014\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4014\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tseen := map[ast.Node]bool{}\n\n\tvar collectConds func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool)\n\tcollectConds = func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool) {\n\t\tseen[ifstmt] = true\n\t\t// Bail if any if-statement has an Init statement or side effects in its condition\n\t\tif ifstmt.Init != nil {\n\t\t\treturn nil, false\n\t\t}\n\t\tif code.MayHaveSideEffects(pass, ifstmt.Cond, nil) {\n\t\t\treturn nil, false\n\t\t}\n\n\t\tconds = append(conds, ifstmt.Cond)\n\t\tif elsestmt, ok := ifstmt.Else.(*ast.IfStmt); ok {\n\t\t\treturn collectConds(elsestmt, conds)\n\t\t}\n\t\treturn conds, true\n\t}\n\tfn := func(node ast.Node) {\n\t\tifstmt := node.(*ast.IfStmt)\n\t\tif seen[ifstmt] {\n\t\t\t// this if-statement is part of an if/else-if chain that we've already processed\n\t\t\treturn\n\t\t}\n\t\tif ifstmt.Else == nil {\n\t\t\t// there can be at most one condition\n\t\t\treturn\n\t\t}\n\t\tconds, ok := collectConds(ifstmt, nil)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tif len(conds) < 2 {\n\t\t\treturn\n\t\t}\n\t\tcounts := map[string]int{}\n\t\tfor _, cond := range conds {\n\t\t\ts := report.Render(pass, cond)\n\t\t\tcounts[s]++\n\t\t\tif counts[s] == 2 {\n\t\t\t\treport.Report(pass, cond, \"this condition occurs multiple times in this if/else if chain\")\n\t\t\t}\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.IfStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4014/sa4014_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4014\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4014/testdata/go1.0/CheckRepeatedIfElse/CheckRepeatedIfElse.go",
    "content": "package pkg\n\nfunc fn1(b1, b2 bool) {\n\tif b1 && !b2 {\n\t} else if b1 {\n\t} else if b1 && !b2 { //@ diag(`condition occurs multiple times`)\n\t} else if b1 { //@ diag(`condition occurs multiple times`)\n\t} else {\n\t\tprintln()\n\t}\n}\n\nfunc fn2(b1, b2 bool, ch chan string) {\n\tif b1 && !b2 {\n\t} else if b1 {\n\t} else if <-ch == \"\" {\n\t} else if <-ch == \"\" {\n\t} else {\n\t\tprintln()\n\t}\n}\n\nfunc fn3() {\n\tif gen() {\n\t\tprintln()\n\t} else if gen() {\n\t\tprintln()\n\t}\n}\n\nfunc fn4() {\n\tif s := gen2(); s == \"\" {\n\t} else if s := gen2(); s == \"\" {\n\t\tprintln()\n\t}\n}\n\nfunc fn5() {\n\tvar s string\n\tif s = gen2(); s == \"\" {\n\t} else if s != \"foo\" {\n\t} else if s = gen2(); s == \"\" {\n\t} else if s != \"foo\" {\n\t\tprintln()\n\t}\n}\n\nfunc fn6() {\n\tif true {\n\t} else {\n\t}\n}\n\nfunc gen() bool    { return false }\nfunc gen2() string { return \"\" }\n"
  },
  {
    "path": "staticcheck/sa4015/sa4015.go",
    "content": "package sa4015\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4015\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(checkMathIntRules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Calling functions like \\'math.Ceil\\' on floats converted from integers doesn't do anything useful`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkMathIntRules = map[string]callcheck.Check{\n\t\"math.Ceil\":  pointlessIntMath,\n\t\"math.Floor\": pointlessIntMath,\n\t\"math.IsNaN\": pointlessIntMath,\n\t\"math.Trunc\": pointlessIntMath,\n\t\"math.IsInf\": pointlessIntMath,\n}\n\nfunc pointlessIntMath(call *callcheck.Call) {\n\tif ConvertedFromInt(call.Args[0].Value) {\n\t\tcall.Invalid(fmt.Sprintf(\"calling %s on a converted integer is pointless\", irutil.CallName(call.Instr.Common())))\n\t}\n}\n\nfunc ConvertedFromInt(v callcheck.Value) bool {\n\tconv, ok := v.Value.(*ir.Convert)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn typeutil.NewTypeSet(conv.X.Type()).All(func(t *types.Term) bool {\n\t\tb, ok := t.Type().Underlying().(*types.Basic)\n\t\treturn ok && b.Info()&types.IsInteger != 0\n\t})\n}\n"
  },
  {
    "path": "staticcheck/sa4015/sa4015_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4015\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4015/testdata/go1.0/CheckMathInt/CheckMathInt.go",
    "content": "package pkg\n\nimport \"math\"\n\nfunc fn(x int) {\n\tmath.Ceil(float64(x))      //@ diag(`on a converted integer is pointless`)\n\tmath.Floor(float64(x * 2)) //@ diag(`on a converted integer is pointless`)\n}\n"
  },
  {
    "path": "staticcheck/sa4015/testdata/go1.18/CheckMathInt/CheckMathInt.go",
    "content": "package pkg\n\nimport \"math\"\n\nfunc fn3[S int8 | int16](x S) {\n\tmath.Ceil(float64(x)) //@ diag(`on a converted integer is pointless`)\n}\n\nfunc fn4[S int8 | int16 | float32](x S) {\n\tmath.Ceil(float64(x))\n}\n"
  },
  {
    "path": "staticcheck/sa4016/sa4016.go",
    "content": "package sa4016\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4016\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Certain bitwise operations, such as \\'x ^ 0\\', do not do anything useful`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tbinop := node.(*ast.BinaryExpr)\n\t\tif !typeutil.All(pass.TypesInfo.TypeOf(binop), func(term *types.Term) bool {\n\t\t\tb, ok := term.Type().Underlying().(*types.Basic)\n\t\t\tif !ok {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn (b.Info() & types.IsInteger) != 0\n\t\t}) {\n\t\t\treturn\n\t\t}\n\t\tswitch binop.Op {\n\t\tcase token.AND, token.OR, token.XOR:\n\t\tdefault:\n\t\t\t// we do not flag shifts because too often, x<<0 is part\n\t\t\t// of a pattern, x<<0, x<<8, x<<16, ...\n\t\t\treturn\n\t\t}\n\t\tif y, ok := binop.Y.(*ast.Ident); ok {\n\t\t\tobj, ok := pass.TypesInfo.ObjectOf(y).(*types.Const)\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif obj.Pkg() != pass.Pkg {\n\t\t\t\t// identifier was dot-imported\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif v, _ := constant.Int64Val(obj.Val()); v != 0 {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpath, _ := astutil.PathEnclosingInterval(code.File(pass, obj), obj.Pos(), obj.Pos())\n\t\t\tif len(path) < 2 {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tspec, ok := path[1].(*ast.ValueSpec)\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(spec.Names) != 1 || len(spec.Values) != 1 {\n\t\t\t\t// TODO(dh): we could support this\n\t\t\t\treturn\n\t\t\t}\n\t\t\tident, ok := spec.Values[0].(*ast.Ident)\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !isIota(pass.TypesInfo.ObjectOf(ident)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tswitch binop.Op {\n\t\t\tcase token.AND:\n\t\t\t\treport.Report(pass, node,\n\t\t\t\t\tfmt.Sprintf(\"%s always equals 0; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?\", report.Render(pass, binop), report.Render(pass, binop.Y), report.Render(pass, binop.Y)))\n\t\t\tcase token.OR, token.XOR:\n\t\t\t\treport.Report(pass, node,\n\t\t\t\t\tfmt.Sprintf(\"%s always equals %s; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?\", report.Render(pass, binop), report.Render(pass, binop.X), report.Render(pass, binop.Y), report.Render(pass, binop.Y)))\n\t\t\t}\n\t\t} else if code.IsIntegerLiteral(pass, binop.Y, constant.MakeInt64(0)) {\n\t\t\tswitch binop.Op {\n\t\t\tcase token.AND:\n\t\t\t\treport.Report(pass, node, fmt.Sprintf(\"%s always equals 0\", report.Render(pass, binop)))\n\t\t\tcase token.OR, token.XOR:\n\t\t\t\treport.Report(pass, node, fmt.Sprintf(\"%s always equals %s\", report.Render(pass, binop), report.Render(pass, binop.X)))\n\t\t\t}\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.BinaryExpr)(nil))\n\treturn nil, nil\n}\n\nfunc isIota(obj types.Object) bool {\n\tif obj.Name() != \"iota\" {\n\t\treturn false\n\t}\n\tc, ok := obj.(*types.Const)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn c.Pkg() == nil\n}\n"
  },
  {
    "path": "staticcheck/sa4016/sa4016_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4016\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4016/testdata/go1.0/CheckSillyBitwiseOps/CheckSillyBitwiseOps.go",
    "content": "package pkg\n\nconst a = 0\n\nconst (\n\tb = iota\n\tc\n)\n\nconst (\n\ty = 42\n\n\td = iota\n)\n\nfunc fn(x int) {\n\tprintln(x | 0)        //@ diag(`x | 0 always equals x`)\n\tprintln(x & 0)        //@ diag(`x & 0 always equals 0`)\n\tprintln(x ^ 0)        //@ diag(`x ^ 0 always equals x`)\n\tprintln((x << 5) | 0) //@ diag(`(x << 5) | 0 always equals (x << 5)`)\n\tprintln(x | 1)\n\tprintln(x << 0)\n\n\tprintln(x | a)\n\tprintln(x | b) //@ diag(`x | b always equals x; b is defined as iota`)\n\tprintln(x & b) //@ diag(`x & b always equals 0; b is defined as iota`)\n\tprintln(x | c)\n\n\t// d is iota, but its value is 1\n\tprintln(x | d)\n}\n"
  },
  {
    "path": "staticcheck/sa4016/testdata/go1.0/CheckSillyBitwiseOps_dotImport/foo.go",
    "content": "package foo\n\nconst X = 0\n"
  },
  {
    "path": "staticcheck/sa4016/testdata/go1.0/CheckSillyBitwiseOps_dotImport/foo_test.go",
    "content": "package foo_test\n\nimport . \"example.com/CheckSillyBitwiseOps_dotImport\"\n\nvar _ = 1 | X\n"
  },
  {
    "path": "staticcheck/sa4016/testdata/go1.0/CheckSillyBitwiseOps_shadowedIota/shadowed.go",
    "content": "package pkg\n\nconst iota = 0\n\nconst (\n\ta = iota\n)\n\nfunc fn(x int) {\n\t_ = x | a\n}\n"
  },
  {
    "path": "staticcheck/sa4016/testdata/go1.18/CheckSillyBitwiseOps/generics.go",
    "content": "package pkg\n\nfunc tpfn[T int](x T) {\n\t_ = x & 0 //@ diag(`always equals 0`)\n}\n"
  },
  {
    "path": "staticcheck/sa4017/sa4017.go",
    "content": "package sa4017\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/purity\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4017\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer, purity.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Discarding the return values of a function without side effects, making the call pointless`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tpure := pass.ResultOf[purity.Analyzer].(purity.Result)\n\nfnLoop:\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tif code.IsInTest(pass, fn) {\n\t\t\tparams := fn.Signature.Params()\n\t\t\tfor param := range params.Variables() {\n\t\t\t\tif typeutil.IsPointerToTypeWithName(param.Type(), \"testing.B\") {\n\t\t\t\t\t// Ignore discarded pure functions in code related\n\t\t\t\t\t// to benchmarks. Instead of matching BenchmarkFoo\n\t\t\t\t\t// functions, we match any function accepting a\n\t\t\t\t\t// *testing.B. Benchmarks sometimes call generic\n\t\t\t\t\t// functions for doing the actual work, and\n\t\t\t\t\t// checking for the parameter is a lot easier and\n\t\t\t\t\t// faster than analyzing call trees.\n\t\t\t\t\tcontinue fnLoop\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor _, b := range fn.Blocks {\n\t\t\tfor _, ins := range b.Instrs {\n\t\t\t\tins, ok := ins.(*ir.Call)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\trefs := ins.Referrers()\n\t\t\t\tif refs == nil || len(irutil.FilterDebug(*refs)) > 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tcallee := ins.Common().StaticCallee()\n\t\t\t\tif callee == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif callee.Object() == nil {\n\t\t\t\t\t// TODO(dh): support anonymous functions\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif _, ok := pure[callee.Object().(*types.Func)]; ok {\n\t\t\t\t\tif pass.Pkg.Path() == \"fmt_test\" && callee.Object().(*types.Func).FullName() == \"fmt.Sprintf\" {\n\t\t\t\t\t\t// special case for benchmarks in the fmt package\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\treport.Report(pass, ins, fmt.Sprintf(\"%s doesn't have side effects and its return value is ignored\", callee.Object().Name()))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4017/sa4017_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4017\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4017/testdata/go1.0/CheckSideEffectFreeCalls/CheckSideEffectFreeCalls.go",
    "content": "package pkg\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc fn1() {\n\tstrings.Replace(\"\", \"\", \"\", 1) //@ diag(`doesn't have side effects`)\n\tfoo(1, 2)                      //@ diag(`doesn't have side effects`)\n\tbaz(1, 2)                      //@ diag(`doesn't have side effects`)\n\t_, x := baz(1, 2)\n\t_ = x\n\tbar(1, 2)\n}\n\nfunc fn2() {\n\tr, _ := http.NewRequest(\"GET\", \"/\", nil)\n\tr.WithContext(context.Background()) //@ diag(`doesn't have side effects`)\n}\n\nfunc foo(a, b int) int        { return a + b }\nfunc baz(a, b int) (int, int) { return a + b, a + b }\nfunc bar(a, b int) int {\n\tprintln(a + b)\n\treturn a + b\n}\n\nfunc empty()            {}\nfunc stubPointer() *int { return nil }\nfunc stubInt() int      { return 0 }\n\nfunc fn3() {\n\tempty()\n\tstubPointer()\n\tstubInt()\n}\n\nfunc fn4() error {\n\t// Test for https://github.com/dominikh/go-tools/issues/949\n\tif true {\n\t\treturn fmt.Errorf(\"\")\n\t}\n\tfor {\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4017/testdata/go1.0/CheckSideEffectFreeCalls/CheckSideEffectFreeCalls_test.go",
    "content": "package pkg\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestFoo(t *testing.T) {\n\tstrings.Replace(\"\", \"\", \"\", 1) //@ diag(`doesn't have side effects`)\n}\n\nfunc BenchmarkFoo(b *testing.B) {\n\tstrings.Replace(\"\", \"\", \"\", 1)\n}\n\nfunc doBenchmark(s string, b *testing.B) {\n\tstrings.Replace(\"\", \"\", \"\", 1)\n}\n\nfunc BenchmarkBar(b *testing.B) {\n\tdoBenchmark(\"\", b)\n}\n"
  },
  {
    "path": "staticcheck/sa4018/sa4018.go",
    "content": "package sa4018\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"reflect\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/facts/purity\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4018\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer, purity.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Self-assignment of variables`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tpure := pass.ResultOf[purity.Analyzer].(purity.Result)\n\n\tfn := func(node ast.Node) {\n\t\tassign := node.(*ast.AssignStmt)\n\t\tif assign.Tok != token.ASSIGN || len(assign.Lhs) != len(assign.Rhs) {\n\t\t\treturn\n\t\t}\n\t\tfor i, lhs := range assign.Lhs {\n\t\t\trhs := assign.Rhs[i]\n\t\t\tif reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif code.MayHaveSideEffects(pass, lhs, pure) || code.MayHaveSideEffects(pass, rhs, pure) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\trlh := report.Render(pass, lhs)\n\t\t\trrh := report.Render(pass, rhs)\n\t\t\tif rlh == rrh {\n\t\t\t\treport.Report(pass, assign, fmt.Sprintf(\"self-assignment of %s to %s\", rrh, rlh), report.FilterGenerated())\n\t\t\t}\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.AssignStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4018/sa4018_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4018\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4018/testdata/go1.0/CheckSelfAssignment/CheckSelfAssignment.go",
    "content": "package pkg\n\nfunc fn(x int) {\n\tvar z int\n\tvar y int\n\tx = x             //@ diag(`self-assignment`)\n\ty = y             //@ diag(`self-assignment`)\n\ty, x, z = y, x, 1 //@ diag(`self-assignment of y to y`), diag(`self-assignment of x to x`)\n\ty = x\n\t_ = y\n\t_ = x\n\t_ = z\n\tfunc() {\n\t\tx := x\n\t\tprintln(x)\n\t}()\n}\n\nfunc fn1() {\n\tvar (\n\t\tx  []byte\n\t\tch chan int\n\t)\n\tx[42] = x[42]                         //@ diag(`self-assignment`)\n\tx[pure(42)] = x[pure(42)]             //@ diag(`self-assignment`)\n\tx[pure(pure(42))] = x[pure(pure(42))] //@ diag(`self-assignment`)\n\tx[impure(42)] = x[impure(42)]\n\tx[impure(pure(42))] = x[impure(pure(42))]\n\tx[pure(impure(42))] = x[pure(impure(42))]\n\tx[pure(<-ch)] = x[pure(<-ch)]\n\tx[pure(pure(<-ch))] = x[pure(pure(<-ch))]\n\tx[<-ch] = x[<-ch]\n\n\ttype T struct {\n\t\tx []int\n\t}\n\tvar ts []T\n\tts[impure(42)].x = ts[impure(42)].x\n\tm := map[*int]int{}\n\tm[ptr1()] = m[ptr1()]\n\tm[ptr2()] = m[ptr2()]\n\tm[new(int)] = m[new(int)]\n\n\tm2 := map[int]int{}\n\tm2[len(x)] = m2[len(x)] //@ diag(`self-assignment`)\n\n\tgen1()[0] = gen1()[0]\n\tgen2(0)[0] = gen2(0)[0] //@ diag(`self-assignment`)\n\tgen3(0)[0] = gen3(0)[0]\n}\n\nfunc ptr1() *int {\n\treturn new(int)\n}\n\nfunc ptr2() *int {\n\tx := 0\n\treturn &x\n}\n\nfunc gen1() []int {\n\treturn nil\n}\n\nfunc gen2(x int) []int {\n\treturn nil\n}\n\nfunc gen3(x int) []int {\n\treturn make([]int, 0)\n}\n\nfunc pure(n int) int {\n\treturn n\n}\n\nfunc impure(n int) int {\n\tprintln(n)\n\treturn n\n}\n"
  },
  {
    "path": "staticcheck/sa4019/sa4019.go",
    "content": "package sa4019\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4019\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Multiple, identical build constraints in the same file`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc buildTagsIdentical(s1, s2 []string) bool {\n\tif len(s1) != len(s2) {\n\t\treturn false\n\t}\n\ts1s := make([]string, len(s1))\n\tcopy(s1s, s1)\n\tsort.Strings(s1s)\n\ts2s := make([]string, len(s2))\n\tcopy(s2s, s2)\n\tsort.Strings(s2s)\n\tfor i, s := range s1s {\n\t\tif s != s2s[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, f := range pass.Files {\n\t\tconstraints := buildTags(f)\n\t\tfor i, constraint1 := range constraints {\n\t\t\tfor j, constraint2 := range constraints {\n\t\t\t\tif i >= j {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif buildTagsIdentical(constraint1, constraint2) {\n\t\t\t\t\tmsg := fmt.Sprintf(\"identical build constraints %q and %q\",\n\t\t\t\t\t\tstrings.Join(constraint1, \" \"),\n\t\t\t\t\t\tstrings.Join(constraint2, \" \"))\n\t\t\t\t\treport.Report(pass, f, msg, report.FilterGenerated(), report.ShortRange())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n\nfunc buildTags(f *ast.File) [][]string {\n\tvar out [][]string\n\tfor line := range strings.SplitSeq(astutil.Preamble(f), \"\\n\") {\n\t\tif !strings.HasPrefix(line, \"+build \") {\n\t\t\tcontinue\n\t\t}\n\t\tline = strings.TrimSpace(strings.TrimPrefix(line, \"+build \"))\n\t\tfields := strings.Fields(line)\n\t\tout = append(out, fields)\n\t}\n\treturn out\n}\n"
  },
  {
    "path": "staticcheck/sa4019/sa4019_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4019\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4019/testdata/go1.1/CheckDuplicateBuildConstraints/CheckDuplicateBuildConstraints.go",
    "content": "//go:build (one || two || three || go1.1) && (three || one || two || go1.1)\n// +build one two three go1.1\n// +build three one two go1.1\n\npackage pkg //@ diag(`identical build constraints`)\n"
  },
  {
    "path": "staticcheck/sa4020/sa4020.go",
    "content": "package sa4020\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/exp/typeparams\"\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4020\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Unreachable case clause in a type switch`,\n\t\tText: `In a type switch like the following\n\n    type T struct{}\n    func (T) Read(b []byte) (int, error) { return 0, nil }\n\n    var v any = T{}\n\n    switch v.(type) {\n    case io.Reader:\n        // ...\n    case T:\n        // unreachable\n    }\n\nthe second case clause can never be reached because \\'T\\' implements\n\\'io.Reader\\' and case clauses are evaluated in source order.\n\nAnother example:\n\n    type T struct{}\n    func (T) Read(b []byte) (int, error) { return 0, nil }\n    func (T) Close() error { return nil }\n\n    var v any = T{}\n\n    switch v.(type) {\n    case io.Reader:\n        // ...\n    case io.ReadCloser:\n        // unreachable\n    }\n\nEven though \\'T\\' has a \\'Close\\' method and thus implements \\'io.ReadCloser\\',\n\\'io.Reader\\' will always match first. The method set of \\'io.Reader\\' is a\nsubset of \\'io.ReadCloser\\'. Thus it is impossible to match the second\ncase without matching the first case.\n\n\nStructurally equivalent interfaces\n\nA special case of the previous example are structurally identical\ninterfaces. Given these declarations\n\n    type T error\n    type V error\n\n    func doSomething() error {\n        err, ok := doAnotherThing()\n        if ok {\n            return T(err)\n        }\n\n        return U(err)\n    }\n\nthe following type switch will have an unreachable case clause:\n\n    switch doSomething().(type) {\n    case T:\n        // ...\n    case V:\n        // unreachable\n    }\n\n\\'T\\' will always match before V because they are structurally equivalent\nand therefore \\'doSomething()\\''s return value implements both.`,\n\t\tSince:    \"2019.2\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// Check if T subsumes V in a type switch. T subsumes V if T is an interface and T's method set is a subset of V's method set.\n\tsubsumes := func(T, V types.Type) bool {\n\t\tif typeparams.IsTypeParam(T) {\n\t\t\treturn false\n\t\t}\n\t\ttIface, ok := T.Underlying().(*types.Interface)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\n\t\treturn types.Implements(V, tIface)\n\t}\n\n\tsubsumesAny := func(Ts, Vs []types.Type) (types.Type, types.Type, bool) {\n\t\tfor _, T := range Ts {\n\t\t\tfor _, V := range Vs {\n\t\t\t\tif subsumes(T, V) {\n\t\t\t\t\treturn T, V, true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn nil, nil, false\n\t}\n\n\tfn := func(node ast.Node) {\n\t\ttsStmt := node.(*ast.TypeSwitchStmt)\n\n\t\ttype ccAndTypes struct {\n\t\t\tcc    *ast.CaseClause\n\t\t\ttypes []types.Type\n\t\t}\n\n\t\t// All asserted types in the order of case clauses.\n\t\tccs := make([]ccAndTypes, 0, len(tsStmt.Body.List))\n\t\tfor _, stmt := range tsStmt.Body.List {\n\t\t\tcc, _ := stmt.(*ast.CaseClause)\n\n\t\t\t// Exclude the 'default' case.\n\t\t\tif len(cc.List) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tTs := make([]types.Type, 0, len(cc.List))\n\t\t\tfor _, expr := range cc.List {\n\t\t\t\t// Exclude the 'nil' value from any 'case' statement (it is always reachable).\n\t\t\t\tif typ := pass.TypesInfo.TypeOf(expr); typ != types.Typ[types.UntypedNil] {\n\t\t\t\t\tTs = append(Ts, typ)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tccs = append(ccs, ccAndTypes{cc: cc, types: Ts})\n\t\t}\n\n\t\tif len(ccs) <= 1 {\n\t\t\t// Zero or one case clauses, nothing to check.\n\t\t\treturn\n\t\t}\n\n\t\t// Check if case clauses following cc have types that are subsumed by cc.\n\t\tfor i, cc := range ccs[:len(ccs)-1] {\n\t\t\tfor _, next := range ccs[i+1:] {\n\t\t\t\tif T, V, yes := subsumesAny(cc.types, next.types); yes {\n\t\t\t\t\treport.Report(pass, next.cc, fmt.Sprintf(\"unreachable case clause: %s will always match before %s\", T.String(), V.String()),\n\t\t\t\t\t\treport.ShortRange())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tcode.Preorder(pass, fn, (*ast.TypeSwitchStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4020/sa4020_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4020\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4020/testdata/go1.0/CheckUnreachableTypeCases/CheckUnreachableTypeCases.go",
    "content": "package pkg\n\nimport \"io\"\n\ntype T struct{}\n\nfunc (T) Read(b []byte) (int, error) { return 0, nil }\nfunc (T) something() string          { return \"non-exported method\" }\n\ntype V error\ntype U error\n\nfunc fn1() {\n\tvar (\n\t\tv   interface{}\n\t\terr error\n\t)\n\n\tswitch v.(type) {\n\tcase io.Reader:\n\t\tprintln(\"io.Reader\")\n\tcase io.ReadCloser: //@ diag(`unreachable case clause: io.Reader will always match before io.ReadCloser`)\n\t\tprintln(\"io.ReadCloser\")\n\t}\n\n\tswitch v.(type) {\n\tcase io.Reader:\n\t\tprintln(\"io.Reader\")\n\tcase T: //@ diag(`unreachable case clause: io.Reader will always match before example.com/CheckUnreachableTypeCases.T`)\n\t\tprintln(\"T\")\n\t}\n\n\tswitch v.(type) {\n\tcase io.Reader:\n\t\tprintln(\"io.Reader\")\n\tcase io.ReadCloser: //@ diag(`unreachable case clause: io.Reader will always match before io.ReadCloser`)\n\t\tprintln(\"io.ReadCloser\")\n\tcase T: //@ diag(`unreachable case clause: io.Reader will always match before example.com/CheckUnreachableTypeCases.T`)\n\t\tprintln(\"T\")\n\t}\n\n\tswitch v.(type) {\n\tcase io.Reader:\n\t\tprintln(\"io.Reader\")\n\tcase io.ReadCloser, T: //@ diag(`unreachable case clause: io.Reader will always match before io.ReadCloser`)\n\t\tprintln(\"io.ReadCloser or T\")\n\t}\n\n\tswitch v.(type) {\n\tcase io.ReadCloser, io.Reader:\n\t\tprintln(\"io.ReadCloser or io.Reader\")\n\tcase T: //@ diag(`unreachable case clause: io.Reader will always match before example.com/CheckUnreachableTypeCases.T`)\n\t\tprintln(\"T\")\n\t}\n\n\tswitch v.(type) {\n\tdefault:\n\t\tprintln(\"something else\")\n\tcase io.Reader:\n\t\tprintln(\"io.Reader\")\n\tcase T: //@ diag(`unreachable case clause: io.Reader will always match before example.com/CheckUnreachableTypeCases.T`)\n\t\tprintln(\"T\")\n\t}\n\n\tswitch v.(type) {\n\tcase interface{}:\n\t\tprintln(\"interface{}\")\n\tcase nil, T: //@ diag(`unreachable case clause: interface{} will always match before example.com/CheckUnreachableTypeCases.T`)\n\t\tprintln(\"nil or T\")\n\t}\n\n\tswitch err.(type) {\n\tcase V:\n\t\tprintln(\"V\")\n\tcase U: //@ diag(`unreachable case clause: example.com/CheckUnreachableTypeCases.V will always match before example.com/CheckUnreachableTypeCases.U`)\n\t\tprintln(\"U\")\n\t}\n\n\tswitch err.(type) {\n\tcase U:\n\t\tprintln(\"U\")\n\tcase V: //@ diag(`unreachable case clause: example.com/CheckUnreachableTypeCases.U will always match before example.com/CheckUnreachableTypeCases.V`)\n\t\tprintln(\"V\")\n\t}\n}\n\nfunc fn3() {\n\tvar (\n\t\tv   interface{}\n\t\terr error\n\t)\n\n\tswitch v.(type) {\n\tcase T:\n\t\tprintln(\"T\")\n\tcase io.Reader:\n\t\tprintln(\"io.Reader\")\n\t}\n\n\tswitch v.(type) {\n\tcase io.ReadCloser:\n\t\tprintln(\"io.ReadCloser\")\n\tcase T:\n\t\tprintln(\"T\")\n\t}\n\n\tswitch v.(type) {\n\tcase io.ReadCloser:\n\t\tprintln(\"io.ReadCloser\")\n\tcase io.Reader:\n\t\tprintln(\"io.Reader\")\n\t}\n\n\tswitch v.(type) {\n\tcase T:\n\t\tprintln(\"T\")\n\t}\n\n\tswitch err.(type) {\n\tcase V, U:\n\t\tprintln(\"V or U\")\n\tcase io.Reader:\n\t\tprintln(\"io.Reader\")\n\t}\n\n\tswitch v.(type) {\n\tdefault:\n\t\tprintln(\"something\")\n\t}\n\n\tswitch v.(type) {\n\tcase interface{}:\n\t\tprintln(\"interface{}\")\n\tcase nil:\n\t\tprintln(\"nil\")\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4020/testdata/go1.18/CheckUnreachableTypeCases/typeparams.go",
    "content": "package pkg\n\nfunc tp1[T any](x interface{}) {\n\tswitch x.(type) {\n\tcase T:\n\tcase int:\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4021/sa4021.go",
    "content": "package sa4021\n\nimport (\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4021\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `\\\"x = append(y)\\\" is equivalent to \\\"x = y\\\"`,\n\t\tSince:    \"2019.2\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar checkSingleArgAppendQ = pattern.MustParse(`(CallExpr (Builtin \"append\") [_])`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node := range code.Matches(pass, checkSingleArgAppendQ) {\n\t\treport.Report(pass, node, \"x = append(y) is equivalent to x = y\", report.FilterGenerated())\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4021/sa4021_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4021\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4021/testdata/go1.0/CheckSingleArgAppend/CheckSingleArgAppend.go",
    "content": "package pkg\n\nfunc fn(arg []int) {\n\tx := append(arg) //@ diag(`x = append(y) is equivalent to x = y`)\n\t_ = x\n\ty := append(arg, 1)\n\t_ = y\n\targ = append(arg) //@ diag(`x = append(y) is equivalent to x = y`)\n\targ = append(arg, 1, 2, 3)\n\tvar nilly []int\n\targ = append(arg, nilly...)\n\targ = append(arg, arg...)\n\n\tappend := func([]int) []int { return nil }\n\targ = append(arg)\n}\n"
  },
  {
    "path": "staticcheck/sa4022/sa4022.go",
    "content": "package sa4022\n\nimport (\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4022\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Comparing the address of a variable against nil`,\n\t\tText:     `Code such as \\\"if &x == nil\\\" is meaningless, because taking the address of a variable always yields a non-nil pointer.`,\n\t\tSince:    \"2020.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar CheckAddressIsNilQ = pattern.MustParse(\n\t`(BinaryExpr\n\t\t(UnaryExpr \"&\" _)\n\t\t(Or \"==\" \"!=\")\n\t\t(Builtin \"nil\"))`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node := range code.Matches(pass, CheckAddressIsNilQ) {\n\t\treport.Report(pass, node, \"the address of a variable cannot be nil\")\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4022/sa4022_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4022\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4022/testdata/go1.0/CheckAddressIsNil/CheckAddressIsNil.go",
    "content": "package pkg\n\nfunc fn(x int, y *int) {\n\t_ = &x == nil //@ diag(`the address of a variable cannot be nil`)\n\t_ = &y != nil //@ diag(`the address of a variable cannot be nil`)\n\n\tif &x != nil { //@ diag(`the address of a variable cannot be nil`)\n\t\tprintln(\"obviously.\")\n\t}\n\n\tif y == nil {\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4023/sa4023.go",
    "content": "package sa4023\n\nimport (\n\t\"fmt\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/nilness\"\n\t\"honnef.co/go/tools/analysis/facts/typedness\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/exp/typeparams\"\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4023\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer, typedness.Analysis, nilness.Analysis},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Impossible comparison of interface value with untyped nil`,\n\t\tText: `Under the covers, interfaces are implemented as two elements, a\ntype T and a value V. V is a concrete value such as an int,\nstruct or pointer, never an interface itself, and has type T. For\ninstance, if we store the int value 3 in an interface, the\nresulting interface value has, schematically, (T=int, V=3). The\nvalue V is also known as the interface's dynamic value, since a\ngiven interface variable might hold different values V (and\ncorresponding types T) during the execution of the program.\n\nAn interface value is nil only if the V and T are both\nunset, (T=nil, V is not set), In particular, a nil interface will\nalways hold a nil type. If we store a nil pointer of type *int\ninside an interface value, the inner type will be *int regardless\nof the value of the pointer: (T=*int, V=nil). Such an interface\nvalue will therefore be non-nil even when the pointer value V\ninside is nil.\n\nThis situation can be confusing, and arises when a nil value is\nstored inside an interface value such as an error return:\n\n    func returnsError() error {\n        var p *MyError = nil\n        if bad() {\n            p = ErrBad\n        }\n        return p // Will always return a non-nil error.\n    }\n\nIf all goes well, the function returns a nil p, so the return\nvalue is an error interface value holding (T=*MyError, V=nil).\nThis means that if the caller compares the returned error to nil,\nit will always look as if there was an error even if nothing bad\nhappened. To return a proper nil error to the caller, the\nfunction must return an explicit nil:\n\n    func returnsError() error {\n        if bad() {\n            return ErrBad\n        }\n        return nil\n    }\n\nIt's a good idea for functions that return errors always to use\nthe error type in their signature (as we did above) rather than a\nconcrete type such as \\'*MyError\\', to help guarantee the error is\ncreated correctly. As an example, \\'os.Open\\' returns an error even\nthough, if not nil, it's always of concrete type *os.PathError.\n\nSimilar situations to those described here can arise whenever\ninterfaces are used. Just keep in mind that if any concrete value\nhas been stored in the interface, the interface will not be nil.\nFor more information, see The Laws of\nReflection at https://golang.org/doc/articles/laws_of_reflection.html.\n\nThis text has been copied from\nhttps://golang.org/doc/faq#nil_error, licensed under the Creative\nCommons Attribution 3.0 License.`,\n\t\tSince:    \"2020.2\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny, // TODO should this be MergeIfAll?\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// The comparison 'fn() == nil' can never be true if fn() returns\n\t// an interface value and only returns typed nils. This is usually\n\t// a mistake in the function itself, but all we can say for\n\t// certain is that the comparison is pointless.\n\t//\n\t// Flag results if no untyped nils are being returned, but either\n\t// known typed nils, or typed unknown nilness are being returned.\n\n\tirpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR)\n\ttypedness := pass.ResultOf[typedness.Analysis].(*typedness.Result)\n\tnilness := pass.ResultOf[nilness.Analysis].(*nilness.Result)\n\tfor _, fn := range irpkg.SrcFuncs {\n\t\tfor _, b := range fn.Blocks {\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\tbinop, ok := instr.(*ir.BinOp)\n\t\t\t\tif !ok || !(binop.Op == token.EQL || binop.Op == token.NEQ) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif _, ok := binop.X.Type().Underlying().(*types.Interface); !ok || typeparams.IsTypeParam(binop.X.Type()) {\n\t\t\t\t\t// TODO support swapped X and Y\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tk, ok := binop.Y.(*ir.Const)\n\t\t\t\tif !ok || !k.IsNil() {\n\t\t\t\t\t// if binop.X is an interface, then binop.Y can\n\t\t\t\t\t// only be a Const if its untyped. A typed nil\n\t\t\t\t\t// constant would first be passed to\n\t\t\t\t\t// MakeInterface.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tvar idx int\n\t\t\t\tvar obj *types.Func\n\t\t\t\tswitch x := irutil.Flatten(binop.X).(type) {\n\t\t\t\tcase *ir.Call:\n\t\t\t\t\tcallee := x.Call.StaticCallee()\n\t\t\t\t\tif callee == nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tobj, _ = callee.Object().(*types.Func)\n\t\t\t\t\tidx = 0\n\t\t\t\tcase *ir.Extract:\n\t\t\t\t\tcall, ok := irutil.Flatten(x.Tuple).(*ir.Call)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tcallee := call.Call.StaticCallee()\n\t\t\t\t\tif callee == nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tobj, _ = callee.Object().(*types.Func)\n\t\t\t\t\tidx = x.Index\n\t\t\t\tcase *ir.MakeInterface:\n\t\t\t\t\tvar qualifier string\n\t\t\t\t\tswitch binop.Op {\n\t\t\t\t\tcase token.EQL:\n\t\t\t\t\t\tqualifier = \"never\"\n\t\t\t\t\tcase token.NEQ:\n\t\t\t\t\t\tqualifier = \"always\"\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tpanic(\"unreachable\")\n\t\t\t\t\t}\n\n\t\t\t\t\tterms, err := typeparams.NormalTerms(x.X.Type())\n\t\t\t\t\tif len(terms) == 0 || err != nil {\n\t\t\t\t\t\t// Type is a type parameter with no type terms (or we couldn't determine the terms). Such a type\n\t\t\t\t\t\t// _can_ be nil when put in an interface value.\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tif report.HasRange(x.X) {\n\t\t\t\t\t\treport.Report(pass, binop, fmt.Sprintf(\"this comparison is %s true\", qualifier),\n\t\t\t\t\t\t\treport.Related(x.X, \"the lhs of the comparison gets its value from here and has a concrete type\"))\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// we can't generate related information for this, so make the diagnostic itself slightly more useful\n\t\t\t\t\t\treport.Report(pass, binop, fmt.Sprintf(\"this comparison is %s true; the lhs of the comparison has been assigned a concretely typed value\", qualifier))\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif obj == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tisNil, onlyGlobal := nilness.MayReturnNil(obj, idx)\n\t\t\t\tif typedness.MustReturnTyped(obj, idx) && isNil && !onlyGlobal && !code.IsInTest(pass, binop) {\n\t\t\t\t\t// Don't flag these comparisons in tests. Tests\n\t\t\t\t\t// may be explicitly enforcing the invariant that\n\t\t\t\t\t// a value isn't nil.\n\n\t\t\t\t\tvar qualifier string\n\t\t\t\t\tswitch binop.Op {\n\t\t\t\t\tcase token.EQL:\n\t\t\t\t\t\tqualifier = \"never\"\n\t\t\t\t\tcase token.NEQ:\n\t\t\t\t\t\tqualifier = \"always\"\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tpanic(\"unreachable\")\n\t\t\t\t\t}\n\t\t\t\t\treport.Report(pass, binop, fmt.Sprintf(\"this comparison is %s true\", qualifier),\n\t\t\t\t\t\t// TODO support swapped X and Y\n\t\t\t\t\t\treport.Related(binop.X, fmt.Sprintf(\"the lhs of the comparison is the %s return value of this function call\", report.Ordinal(idx+1))),\n\t\t\t\t\t\treport.Related(obj, fmt.Sprintf(\"%s never returns a nil interface value\", typeutil.FuncName(obj))))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4023/sa4023_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4023\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4023/testdata/go1.0/CheckTypedNilInterface/CheckTypedNilInterface.go",
    "content": "package pkg\n\nimport (\n\t\"errors\"\n\t\"os/exec\"\n)\n\ntype T struct{ x *int }\n\nfunc fn1() *int             { return nil }\nfunc fn2() (int, *int, int) { return 0, nil, 0 }\n\nfunc fn3() (int, error) { return 0, nil }\nfunc fn4() error        { return nil }\n\nfunc gen1() interface{} {\n\t// don't flag, returning a concrete value\n\treturn 0\n}\n\nfunc gen2() interface{} {\n\t// don't flag, returning a concrete value\n\treturn &T{}\n}\n\nfunc gen3() interface{} {\n\t// flag, always returns a typed value\n\tm := map[int]*int{}\n\treturn m[0]\n}\n\nfunc gen4() (int, interface{}, *int) {\n\t// flag ret[1], always a typed value\n\tm := map[int]*int{}\n\treturn 0, m[0], nil\n}\n\nfunc gen5() interface{} {\n\t// flag, propagate gen3\n\treturn gen3()\n}\n\nfunc gen6(b bool) interface{} {\n\t// don't flag, sometimes returns untyped nil\n\tif b {\n\t\tm := map[int]*int{}\n\t\treturn m[0]\n\t} else {\n\t\treturn nil\n\t}\n}\n\nfunc gen7() interface{} {\n\t// flag, always returns a typed value\n\treturn fn1()\n}\n\nfunc gen8(x *int) interface{} {\n\t// flag\n\tif x == nil {\n\t\treturn x\n\t}\n\treturn x\n}\n\nfunc gen9() interface{} {\n\t// flag\n\tvar x *int\n\treturn x\n}\n\nfunc gen10() interface{} {\n\t// flag\n\tvar x *int\n\tif x == nil {\n\t\treturn x\n\t}\n\treturn errors.New(\"\")\n\n\t// This is a tricky one. we should flag this, because it never\n\t// returns a nil error, but if errors.New could return untyped\n\t// nils, then we shouldn't flag it. we need to consider the\n\t// implementation of the called function.\n}\n\nfunc gen11() interface{} {\n\t// don't flag, we sometimes return untyped nil\n\tif true {\n\t\treturn nil\n\t} else {\n\t\treturn (*int)(nil)\n\t}\n}\n\nfunc gen12(b bool) interface{} {\n\t// flag, all branches return typed nils\n\tvar x interface{}\n\tif b {\n\t\tx = (*int)(nil)\n\t} else {\n\t\tx = (*string)(nil)\n\t}\n\treturn x\n}\n\nfunc gen13() interface{} {\n\t// flag, always returns a typed value\n\t_, x, _ := fn2()\n\treturn x\n}\n\nfunc gen14(ch chan *int) interface{} {\n\t// flag\n\treturn <-ch\n}\n\nfunc gen15() interface{} {\n\t// flag\n\tt := &T{}\n\treturn t.x\n}\n\nvar g *int = new(int)\n\nfunc gen16() interface{} {\n\t// don't flag. returning a global is akin to returning &T{}.\n\treturn g\n}\n\nfunc gen17(x interface{}) interface{} {\n\t// don't flag\n\tif x != nil {\n\t\treturn x\n\t}\n\treturn x\n}\n\nfunc gen18() (int, error) {\n\t// don't flag\n\t_, err := fn3()\n\tif err != nil {\n\t\treturn 0, errors.New(\"yo\")\n\t}\n\treturn 0, err\n}\n\nfunc gen19() (out interface{}) {\n\t// don't flag\n\tif true {\n\t\treturn (*int)(nil)\n\t}\n\treturn\n}\n\nfunc gen20() (out interface{}) {\n\t// don't flag\n\tif true {\n\t\treturn (*int)(nil)\n\t}\n\treturn\n}\n\nfunc gen21() error {\n\tif false {\n\t\treturn (*exec.Error)(nil)\n\t}\n\treturn fn4()\n}\n\nfunc gen22() interface{} {\n\tif true {\n\t\treturn g\n\t}\n\treturn (*int)(nil)\n}\n\nfunc test() {\n\t_ = gen1() == nil\n\t_ = gen2() == nil\n\t_ = gen3() == nil //@ diag(`never true`)\n\t{\n\t\t_, r2, r3 := gen4()\n\t\t_ = r2 == nil //@ diag(`never true`)\n\t\t_ = r3 == nil\n\t}\n\t_ = gen5() == nil //@ diag(`never true`)\n\t_ = gen6(false) == nil\n\t_ = gen7() == nil    //@ diag(`never true`)\n\t_ = gen8(nil) == nil //@ diag(`never true`)\n\t_ = gen9() == nil    //@ diag(`never true`)\n\t_ = gen10() == nil   //@ diag(`never true`)\n\t_ = gen11() == nil\n\t_ = gen12(true) == nil //@ diag(`never true`)\n\t_ = gen13() == nil     //@ diag(`never true`)\n\t_ = gen14(nil) == nil  //@ diag(`never true`)\n\t_ = gen15() == nil     //@ diag(`never true`)\n\t_ = gen16() == nil\n\t_ = gen17(nil) == nil\n\t{\n\t\t_, r2 := gen18()\n\t\t_ = r2 == nil\n\t}\n\t_ = gen19() == nil\n\t_ = gen20() == nil\n\t_ = gen21() == nil\n\t_ = gen22() == nil //@ diag(`never true`)\n\n\tvar v1 interface{} = 0\n\t_ = v1 == nil //@ diag(`never true`)\n}\n"
  },
  {
    "path": "staticcheck/sa4023/testdata/go1.0/CheckTypedNilInterface/real.go",
    "content": "package pkg\n\nimport \"log\"\n\ntype iface interface{ m() }\n\ntype t1 struct{ int }\n\nfunc (t *t1) m() { log.Println(t.int) }\n\ntype internalMessage struct{ v *t1 }\n\nfunc f(msg chan internalMessage, input int) {\n\tk := &t1{input}\n\n\tif input > 2 {\n\t\tk = nil\n\t}\n\tmsg <- internalMessage{k}\n\n}\n\nfunc SyncPublicMethod(input int) iface {\n\tch := make(chan internalMessage)\n\tgo f(ch, input)\n\tanswer := <-ch\n\t// Problem: if answer.v == nil then this will created typed nil iface return value\n\treturn answer.v\n}\n\nfunc main() {\n\tfor i := 0; i < 10; i++ {\n\t\tk := SyncPublicMethod(i)\n\t\tif k == nil { //@ diag(`this comparison is never true`)\n\t\t\tlog.Println(\"never printed\")\n\t\t\treturn\n\t\t}\n\n\t\t// Will panic.\n\t\tk.m()\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4023/testdata/go1.0/i26000/26000.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\ntype CustomError struct {\n\tErr string\n}\n\nfunc (ce CustomError) Error() string {\n\treturn ce.Err\n}\n\nfunc SomeFunc() (string, *CustomError) {\n\treturn \"hello\", nil\n}\n\nfunc main() {\n\t// Do something that creates a variable err of type error\n\t_, err := os.Open(\"/\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Then replace the err type with *CustomError\n\tval, err := SomeFunc()\n\tif err != nil { //@ diag(`this comparison is always true`)\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"No problem\", val)\n}\n"
  },
  {
    "path": "staticcheck/sa4023/testdata/go1.0/i27815/27815.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\ntype MyError struct {\n\tx string\n}\n\nfunc (e *MyError) Error() string {\n\treturn e.x\n}\n\nfunc f() *MyError {\n\treturn nil\n}\n\nfunc main() {\n\tvar e error\n\te = f()\n\t// e should be nil ?\n\tif e != nil { //@ diag(`this comparison is always true`)\n\t\tfmt.Println(\"NOT NIL\")\n\t} else {\n\t\tfmt.Println(\"NIL\")\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4023/testdata/go1.0/i28241/28241.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\ntype Nil interface {\n\tString() string\n}\n\nfunc MakeNil() Nil {\n\tvar n *NilStruct\n\treturn n\n}\n\ntype NilStruct struct {\n\tData string\n}\n\nfunc (n *NilStruct) String() string {\n\treturn n.Data\n}\n\nfunc main() {\n\tvar n *NilStruct\n\tfmt.Printf(\"%t %#v %s %t\\n\",\n\t\tn == nil,\n\t\tn,\n\t\treflect.ValueOf(n).Kind(),\n\t\treflect.ValueOf(n).IsNil())\n\tn2 := MakeNil()\n\tfmt.Printf(\"%t %#v %s %t\\n\",\n\t\tn2 == nil, //@ diag(`this comparison is never true`)\n\t\tn2,\n\t\treflect.ValueOf(n2).Kind(),\n\t\treflect.ValueOf(n2).IsNil())\n\tfmt.Println(n2.String())\n}\n"
  },
  {
    "path": "staticcheck/sa4023/testdata/go1.0/i31873/31873.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype S struct{}\n\nfunc (s *S) Error() string {\n\treturn \"error for S\"\n}\n\nfunc structNil() *S {\n\treturn nil\n}\n\nfunc errorNil() error {\n\treturn nil\n}\n\nfunc main() {\n\terr := errorNil()\n\tfmt.Println(err != nil)\n\terr = structNil()\n\tfmt.Println(err != nil) //@ diag(`this comparison is always true`)\n\terr = errorNil()\n\tfmt.Println(err != nil)\n}\n"
  },
  {
    "path": "staticcheck/sa4023/testdata/go1.0/i33965/33965.go",
    "content": "package pkg\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\ntype customError struct {\n}\n\nfunc (p *customError) Error() string {\n\treturn \"custom error\"\n}\n\nfunc getNilCustomError() *customError {\n\treturn nil\n}\n\nfunc TestWebSocketClient_basic(t *testing.T) {\n\terr1 := getNilCustomError()\n\tfmt.Println(err1 == nil) // ok is true\n\n\terr2 := error(nil)\n\terr2 = getNilCustomError()\n\tfmt.Println(err2 == nil) //@ diag(`this comparison is never true`)\n}\n"
  },
  {
    "path": "staticcheck/sa4023/testdata/go1.0/i33994/33994.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tvar err = errors.New(\"errors msg\")\n\tname, err := GetName()\n\tif err != nil { //@ diag(`this comparison is always true`)\n\t\tfmt.Println(err)\n\t} else {\n\t\tfmt.Println(name)\n\t}\n}\n\ntype Error struct {\n\tMessage string\n}\n\nfunc (e *Error) Error() string {\n\tif e == nil {\n\t\treturn \"Error is nil\"\n\t}\n\treturn e.Message\n}\n\nfunc GetName() (string, *Error) {\n\tvar err = &Error{\n\t\tMessage: \"error msg\",\n\t}\n\terr = nil\n\treturn \"yixinin\", err\n}\n"
  },
  {
    "path": "staticcheck/sa4023/testdata/go1.0/i35217/35217.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\ntype someError struct {\n\tMsg string\n}\n\nfunc (e *someError) Error() string {\n\treturn \"someError: \" + e.Msg\n}\n\nfunc calculate() (int, *someError) {\n\treturn 42, nil\n}\n\nfunc main() {\n\terr := errors.New(\"ERROR\")\n\tnum, err := calculate()\n\tfmt.Println(num, err, err == nil) //@ diag(`this comparison is never true`)\n}\n"
  },
  {
    "path": "staticcheck/sa4023/testdata/go1.18/CheckTypedNilInterface/generics.go",
    "content": "package pkg\n\nfunc tpgen1[T *int,]() T {\n\treturn (T)(nil)\n}\n\nfunc bar() {\n\tif tpgen1() == nil {\n\t}\n}\n\nfunc tpfn1[T any](x T) {\n\tif any(x) == nil {\n\t\t// this is entirely possible if baz is instantiated with an interface type for T. For example: baz[error](nil)\n\t}\n}\n\nfunc tpfn2[T ~int](x T) {\n\tif any(x) == nil { //@ diag(`this comparison is never true`)\n\t\t// this is not possible, because T only accepts concrete types\n\t}\n}\n\nfunc tpgen3[T any](x T) any {\n\treturn any(x)\n}\n\nfunc tpgen4[T ~*int](x T) any {\n\treturn any(x)\n}\n\nfunc tptest() {\n\t_ = tpgen1() == nil\n\n\t_ = tpgen3[error](nil) == nil\n\n\t// ideally we'd flag this, but the analysis is generic-insensitive at the moment.\n\t_ = tpgen3[*int](nil) == nil\n\n\t_ = tpgen4[*int](nil) == nil //@ diag(`never true`)\n}\n"
  },
  {
    "path": "staticcheck/sa4023/testdata/go1.9/CheckTypedNilInterface/CheckTypedNilInterface.go",
    "content": "package pkg\n\ntype any = interface{}\n\nfunc test() {\n\t_ = any((*int)(nil)) == nil //@ diag(`never true`)\n\t_ = any((error)(nil)) == nil\n}\n"
  },
  {
    "path": "staticcheck/sa4024/sa4024.go",
    "content": "package sa4024\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4024\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Checking for impossible return value from a builtin function`,\n\t\tText: `Return values of the \\'len\\' and \\'cap\\' builtins cannot be negative.\n\nSee https://golang.org/pkg/builtin/#len and https://golang.org/pkg/builtin/#cap.\n\nExample:\n\n    if len(slice) < 0 {\n        fmt.Println(\"unreachable code\")\n    }`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar builtinLessThanZeroQ = pattern.MustParse(`\n\t(Or\n\t\t(BinaryExpr\n\t\t\t(IntegerLiteral \"0\")\n\t\t\t\">\"\n\t\t\t(CallExpr builtin@(Builtin (Or \"len\" \"cap\")) _))\n\t\t(BinaryExpr\n\t\t\t(CallExpr builtin@(Builtin (Or \"len\" \"cap\")) _)\n\t\t\t\"<\"\n\t\t\t(IntegerLiteral \"0\")))\n`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, matcher := range code.Matches(pass, builtinLessThanZeroQ) {\n\t\tbuiltin := matcher.State[\"builtin\"].(*ast.Ident)\n\t\treport.Report(pass, node, fmt.Sprintf(\"builtin function %s does not return negative values\", builtin.Name))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4024/sa4024_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4024\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4024/testdata/go1.0/CheckBuiltinZeroComparison/CheckBuiltinZeroComparison.go",
    "content": "package pkg\n\nfunc fn1() {\n\tvar foo []int\n\n\tif len(foo) < 0 { //@ diag(`len does not return negative values`)\n\t\tprintln(\"test\")\n\t}\n\n\tswitch {\n\tcase len(foo) < 0: //@ diag(`negative`)\n\t\tprintln(\"test\")\n\t}\n\n\tfor len(foo) < 0 { //@ diag(`negative`)\n\t\tprintln(\"test\")\n\t}\n\n\tprintln(len(foo) < 0) //@ diag(`negative`)\n\n\tif 0 > cap(foo) { //@ diag(`cap does not return negative values`)\n\t\tprintln(\"test\")\n\t}\n\n\tswitch {\n\tcase 0 > cap(foo): //@ diag(`negative`)\n\t\tprintln(\"test\")\n\t}\n\n\tfor 0 > cap(foo) { //@ diag(`negative`)\n\t\tprintln(\"test\")\n\t}\n\n\tprintln(0 > cap(foo)) //@ diag(`negative`)\n}\n\nfunc fn2() {\n\tconst zero = 0\n\tvar foo []int\n\tprintln(len(foo) < zero)\n\tprintln(len(foo) < 1)\n}\n"
  },
  {
    "path": "staticcheck/sa4025/sa4025.go",
    "content": "package sa4025\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4025\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Integer division of literals that results in zero\",\n\t\tText: `When dividing two integer constants, the result will\nalso be an integer. Thus, a division such as \\'2 / 3\\' results in \\'0\\'.\nThis is true for all of the following examples:\n\n\t_ = 2 / 3\n\tconst _ = 2 / 3\n\tconst _ float64 = 2 / 3\n\t_ = float64(2 / 3)\n\nStaticcheck will flag such divisions if both sides of the division are\ninteger literals, as it is highly unlikely that the division was\nintended to truncate to zero. Staticcheck will not flag integer\ndivision involving named constants, to avoid noisy positives.\n`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar integerDivisionQ = pattern.MustParse(`(BinaryExpr (IntegerLiteral _) \"/\" (IntegerLiteral _))`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node := range code.Matches(pass, integerDivisionQ) {\n\t\tval := constant.ToInt(pass.TypesInfo.Types[node.(ast.Expr)].Value)\n\t\tif v, ok := constant.Uint64Val(val); ok && v == 0 {\n\t\t\treport.Report(pass, node, fmt.Sprintf(\"the integer division '%s' results in zero\", report.Render(pass, node)))\n\t\t}\n\n\t\t// TODO: we could offer a suggested fix here, but I am not\n\t\t// sure what it should be. There are many options to choose\n\t\t// from.\n\n\t\t// Note: we experimented with flagging divisions that truncate\n\t\t// (e.g. 4 / 3), but it ran into false positives in Go's\n\t\t// 'time' package, which does this, deliberately:\n\t\t//\n\t\t//   unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay\n\t\t//\n\t\t// The check also found a real bug in other code, but I don't\n\t\t// think we can outright ban this kind of division.\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4025/sa4025_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4025\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4025/testdata/go1.0/CheckIntegerDivisionEqualsZero/CheckIntegerDivisionEqualsZero.go",
    "content": "package pkg\n\nfunc foo(x float64) {}\n\nfunc fn() {\n\t_ = 2 / 3 //@ diag(`results in zero`)\n\t_ = 4 / 2\n\t_ = 4 / 3\n\t_ = 0 / 2 //@ diag(`results in zero`)\n\t_ = 2 / 3.\n\t_ = 2 / 3.0\n\t_ = 2.0 / 3\n\tconst _ = 2 / 3         //@ diag(`results in zero`)\n\tconst _ float64 = 2 / 3 //@ diag(`results in zero`)\n\t_ = float64(2 / 3)      //@ diag(`results in zero`)\n\n\tfoo(1 / 2) //@ diag(`results in zero`)\n}\n"
  },
  {
    "path": "staticcheck/sa4026/sa4026.go",
    "content": "package sa4026\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4026\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Go constants cannot express negative zero\",\n\t\tText: `In IEEE 754 floating point math, zero has a sign and can be positive\nor negative. This can be useful in certain numerical code.\n\nGo constants, however, cannot express negative zero. This means that\nthe literals \\'-0.0\\' and \\'0.0\\' have the same ideal value (zero) and\nwill both represent positive zero at runtime.\n\nTo explicitly and reliably create a negative zero, you can use the\n\\'math.Copysign\\' function: \\'math.Copysign(0, -1)\\'.`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar negativeZeroFloatQ = pattern.MustParse(`\n\t(Or\n\t\t(UnaryExpr\n\t\t\t\"-\"\n\t\t\t(BasicLit \"FLOAT\" \"0.0\"))\n\n\t\t(UnaryExpr\n\t\t\t\"-\"\n\t\t\t(CallExpr conv@(Object (Or \"float32\" \"float64\")) lit@(Or (BasicLit \"INT\" \"0\") (BasicLit \"FLOAT\" \"0.0\"))))\n\n\t\t(CallExpr\n\t\t\tconv@(Object (Or \"float32\" \"float64\"))\n\t\t\t(UnaryExpr \"-\" lit@(BasicLit \"INT\" \"0\"))))`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, negativeZeroFloatQ) {\n\t\tif conv, ok := m.State[\"conv\"].(*types.TypeName); ok {\n\t\t\tvar replacement string\n\t\t\t// TODO(dh): how does this handle type aliases?\n\t\t\tif conv.Name() == \"float32\" {\n\t\t\t\treplacement = `float32(math.Copysign(0, -1))`\n\t\t\t} else {\n\t\t\t\treplacement = `math.Copysign(0, -1)`\n\t\t\t}\n\t\t\treport.Report(pass, node,\n\t\t\t\tfmt.Sprintf(\"in Go, the floating-point expression '%s' is the same as '%s(%s)', it does not produce a negative zero\",\n\t\t\t\t\treport.Render(pass, node),\n\t\t\t\t\tconv.Name(),\n\t\t\t\t\treport.Render(pass, m.State[\"lit\"])),\n\t\t\t\treport.Fixes(edit.Fix(\"Use math.Copysign to create negative zero\", edit.ReplaceWithString(node, replacement))))\n\t\t} else {\n\t\t\tconst replacement = `math.Copysign(0, -1)`\n\t\t\treport.Report(pass, node,\n\t\t\t\t\"in Go, the floating-point literal '-0.0' is the same as '0.0', it does not produce a negative zero\",\n\t\t\t\treport.Fixes(edit.Fix(\"Use math.Copysign to create negative zero\", edit.ReplaceWithString(node, replacement))))\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4026/sa4026_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4026\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4026/testdata/go1.0/CheckNegativeZeroFloat/CheckNegativeZeroFloat.go",
    "content": "package pkg\n\nconst x = 0.0\n\nfunc fn() {\n\t_ = -0.0          //@ diag(`in Go, the floating-point literal '-0.0' is the same as '0.0', it does not produce a negative zero`)\n\t_ = float32(-0.0) //@ diag(`in Go, the floating-point literal '-0.0' is the same as '0.0', it does not produce a negative zero`)\n\t_ = float64(-0.0) //@ diag(`in Go, the floating-point literal '-0.0' is the same as '0.0', it does not produce a negative zero`)\n\t_ = -float32(0)   //@ diag(`in Go, the floating-point expression '-float32(0)' is the same as 'float32(0)', it does not produce a negative zero`)\n\t_ = -float64(0)   //@ diag(`in Go, the floating-point expression '-float64(0)' is the same as 'float64(0)', it does not produce a negative zero`)\n\t_ = -float32(0.0) //@ diag(`in Go, the floating-point expression '-float32(0.0)' is the same as 'float32(0.0)', it does not produce a negative zero`)\n\t_ = -float64(0.0) //@ diag(`in Go, the floating-point expression '-float64(0.0)' is the same as 'float64(0.0)', it does not produce a negative zero`)\n\n\t// intentionally not flagged\n\t_ = -x\n}\n"
  },
  {
    "path": "staticcheck/sa4026/testdata/go1.0/CheckNegativeZeroFloat/CheckNegativeZeroFloat.go.golden",
    "content": "package pkg\n\nconst x = 0.0\n\nfunc fn() {\n\t_ = math.Copysign(0, -1)          //@ diag(`in Go, the floating-point literal '-0.0' is the same as '0.0', it does not produce a negative zero`)\n\t_ = float32(math.Copysign(0, -1)) //@ diag(`in Go, the floating-point literal '-0.0' is the same as '0.0', it does not produce a negative zero`)\n\t_ = float64(math.Copysign(0, -1)) //@ diag(`in Go, the floating-point literal '-0.0' is the same as '0.0', it does not produce a negative zero`)\n\t_ = float32(math.Copysign(0, -1)) //@ diag(`in Go, the floating-point expression '-float32(0)' is the same as 'float32(0)', it does not produce a negative zero`)\n\t_ = math.Copysign(0, -1)          //@ diag(`in Go, the floating-point expression '-float64(0)' is the same as 'float64(0)', it does not produce a negative zero`)\n\t_ = float32(math.Copysign(0, -1)) //@ diag(`in Go, the floating-point expression '-float32(0.0)' is the same as 'float32(0.0)', it does not produce a negative zero`)\n\t_ = math.Copysign(0, -1)          //@ diag(`in Go, the floating-point expression '-float64(0.0)' is the same as 'float64(0.0)', it does not produce a negative zero`)\n\n\t// intentionally not flagged\n\t_ = -x\n}\n"
  },
  {
    "path": "staticcheck/sa4027/sa4027.go",
    "content": "package sa4027\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4027\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `\\'(*net/url.URL).Query\\' returns a copy, modifying it doesn't change the URL`,\n\t\tText: `\\'(*net/url.URL).Query\\' parses the current value of \\'net/url.URL.RawQuery\\'\nand returns it as a map of type \\'net/url.Values\\'. Subsequent changes to\nthis map will not affect the URL unless the map gets encoded and\nassigned to the URL's \\'RawQuery\\'.\n\nAs a consequence, the following code pattern is an expensive no-op:\n\\'u.Query().Add(key, value)\\'.`,\n\t\tSince:    \"2021.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar ineffectiveURLQueryAddQ = pattern.MustParse(`(CallExpr (SelectorExpr (CallExpr (SelectorExpr recv (Ident \"Query\")) []) (Ident meth)) _)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// TODO(dh): We could make this check more complex and detect\n\t// pointless modifications of net/url.Values in general, but that\n\t// requires us to get the state machine correct, else we'll cause\n\t// false positives.\n\n\tfor node, m := range code.Matches(pass, ineffectiveURLQueryAddQ) {\n\t\tif !code.IsOfPointerToTypeWithName(pass, m.State[\"recv\"].(ast.Expr), \"net/url.URL\") {\n\t\t\tcontinue\n\t\t}\n\t\tswitch m.State[\"meth\"].(string) {\n\t\tcase \"Add\", \"Del\", \"Set\":\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\t\treport.Report(pass, node, \"(*net/url.URL).Query returns a copy, modifying it doesn't change the URL\")\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4027/sa4027_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4027\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4027/testdata/go1.0/CheckIneffectiveURLQueryModification/CheckIneffectiveURLQueryModification.go",
    "content": "package pkg\n\nimport \"net/url\"\n\nfunc fn(u *url.URL) {\n\tu.Query().Add(\"\", \"\") //@ diag(`returns a copy`)\n\tu.Query().Set(\"\", \"\") //@ diag(`returns a copy`)\n\tu.Query().Del(\"\")     //@ diag(`returns a copy`)\n\tu.Query().Encode()\n\n\tvar t T\n\tt.Query().Add(\"\", \"\")\n}\n\ntype T struct{}\n\nfunc (v T) Query() T              { return v }\nfunc (v T) Add(arg1, arg2 string) {}\n"
  },
  {
    "path": "staticcheck/sa4028/sa4028.go",
    "content": "package sa4028\n\nimport (\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4028\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `\\'x % 1\\' is always zero`,\n\t\tSince:    \"2022.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar moduloOneQ = pattern.MustParse(`(BinaryExpr _ \"%\" (IntegerLiteral \"1\"))`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node := range code.Matches(pass, moduloOneQ) {\n\t\treport.Report(pass, node, \"x % 1 is always zero\")\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4028/sa4028_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4028\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4028/testdata/go1.0/CheckModuloOne/CheckModuloOne.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar a int\n\tvar b uint\n\t_ = a % 1 //@ diag(`x % 1 is always zero`)\n\t_ = a % 2\n\t_ = b % 1 //@ diag(`x % 1 is always zero`)\n}\n"
  },
  {
    "path": "staticcheck/sa4029/sa4029.go",
    "content": "package sa4029\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4029\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Ineffective attempt at sorting slice\",\n\t\tText: `\n\\'sort.Float64Slice\\', \\'sort.IntSlice\\', and \\'sort.StringSlice\\' are\ntypes, not functions. Doing \\'x = sort.StringSlice(x)\\' does nothing,\nespecially not sort any values. The correct usage is\n\\'sort.Sort(sort.StringSlice(x))\\' or \\'sort.StringSlice(x).Sort()\\',\nbut there are more convenient helpers, namely \\'sort.Float64s\\',\n\\'sort.Ints\\', and \\'sort.Strings\\'.\n`,\n\t\tSince:    \"2022.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar ineffectiveSortQ = pattern.MustParse(`(AssignStmt target@(Ident _) \"=\" (CallExpr typ@(Symbol (Or \"sort.Float64Slice\" \"sort.IntSlice\" \"sort.StringSlice\")) [target]))`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, ineffectiveSortQ) {\n\t\t_, ok := types.Unalias(pass.TypesInfo.TypeOf(m.State[\"target\"].(ast.Expr))).(*types.Slice)\n\t\tif !ok {\n\t\t\t// Avoid flagging 'x = sort.StringSlice(x)' where TypeOf(x) == sort.StringSlice\n\t\t\tcontinue\n\t\t}\n\n\t\tvar alternative string\n\t\ttypeName := types.TypeString(types.Unalias(m.State[\"typ\"].(*types.TypeName).Type()), nil)\n\t\tswitch typeName {\n\t\tcase \"sort.Float64Slice\":\n\t\t\talternative = \"Float64s\"\n\t\tcase \"sort.IntSlice\":\n\t\t\talternative = \"Ints\"\n\t\tcase \"sort.StringSlice\":\n\t\t\talternative = \"Strings\"\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"unreachable: %q\", typeName))\n\t\t}\n\n\t\tr := &ast.CallExpr{\n\t\t\tFun: &ast.SelectorExpr{\n\t\t\t\tX:   &ast.Ident{Name: \"sort\"},\n\t\t\t\tSel: &ast.Ident{Name: alternative},\n\t\t\t},\n\t\t\tArgs: []ast.Expr{m.State[\"target\"].(ast.Expr)},\n\t\t}\n\n\t\treport.Report(pass, node,\n\t\t\tfmt.Sprintf(\"%s is a type, not a function, and %s doesn't sort your values; consider using sort.%s instead\",\n\t\t\t\ttypeName,\n\t\t\t\treport.Render(pass, node.(*ast.AssignStmt).Rhs[0]),\n\t\t\t\talternative),\n\t\t\treport.Fixes(edit.Fix(fmt.Sprintf(\"Replace with call to sort.%s\", alternative), edit.ReplaceWithNode(pass.Fset, node, r))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4029/sa4029_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4029\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4029/testdata/go1.0/CheckIneffectiveSort/CheckIneffectiveSort.go",
    "content": "package pkg\n\nimport \"sort\"\n\nfunc fn() {\n\tvar a []string\n\tvar b []float64\n\tvar c sort.StringSlice\n\n\ta = sort.StringSlice(a)  //@ diag(re`sort\\.StringSlice is a type.+consider using sort\\.Strings instead`)\n\tb = sort.Float64Slice(b) //@ diag(re`sort\\.Float64Slice is a type.+consider using sort\\.Float64s instead`)\n\tc = sort.StringSlice(c)\n}\n"
  },
  {
    "path": "staticcheck/sa4029/testdata/go1.0/CheckIneffectiveSort/CheckIneffectiveSort.go.golden",
    "content": "package pkg\n\nimport \"sort\"\n\nfunc fn() {\n\tvar a []string\n\tvar b []float64\n\tvar c sort.StringSlice\n\n\tsort.Strings(a)  //@ diag(re`sort\\.StringSlice is a type.+consider using sort\\.Strings instead`)\n\tsort.Float64s(b) //@ diag(re`sort\\.Float64Slice is a type.+consider using sort\\.Float64s instead`)\n\tc = sort.StringSlice(c)\n}\n"
  },
  {
    "path": "staticcheck/sa4029/testdata/go1.9/CheckIneffectiveSort/CheckIneffectiveSort.go",
    "content": "package pkg\n\nimport \"sort\"\n\nfunc fn() {\n\ttype Strings = []string\n\tvar d Strings\n\n\td = sort.StringSlice(d) //@ diag(re`sort\\.StringSlice is a type.+consider using sort\\.Strings instead`)\n}\n"
  },
  {
    "path": "staticcheck/sa4029/testdata/go1.9/CheckIneffectiveSort/CheckIneffectiveSort.go.golden",
    "content": "package pkg\n\nimport \"sort\"\n\nfunc fn() {\n\ttype Strings = []string\n\tvar d Strings\n\n\tsort.Strings(d) //@ diag(re`sort\\.StringSlice is a type.+consider using sort\\.Strings instead`)\n}\n"
  },
  {
    "path": "staticcheck/sa4030/sa4030.go",
    "content": "package sa4030\n\nimport (\n\t\"fmt\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4030\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Ineffective attempt at generating random number\",\n\t\tText: `\nFunctions in the \\'math/rand\\' package that accept upper limits, such\nas \\'Intn\\', generate random numbers in the half-open interval [0,n). In\nother words, the generated numbers will be \\'>= 0\\' and \\'< n\\' – they\ndon't include \\'n\\'. \\'rand.Intn(1)\\' therefore doesn't generate \\'0\\'\nor \\'1\\', it always generates \\'0\\'.`,\n\t\tSince:    \"2022.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar ineffectiveRandIntQ = pattern.MustParse(`\n\t(CallExpr\n\t\t(Symbol\n\t\t\tname@(Or\n\t\t\t\t\"math/rand.Int31n\"\n\t\t\t\t\"math/rand.Int63n\"\n\t\t\t\t\"math/rand.Intn\"\n\t\t\t\t\"(*math/rand.Rand).Int31n\"\n\t\t\t\t\"(*math/rand.Rand).Int63n\"\n\t\t\t\t\"(*math/rand.Rand).Intn\"\n\n\t\t\t\t\"math/rand/v2.Int32N\"\n\t\t\t\t\"math/rand/v2.Int64N\"\n\t\t\t\t\"math/rand/v2.IntN\"\n\t\t\t\t\"math/rand/v2.N\"\n\t\t\t\t\"math/rand/v2.Uint32N\"\n\t\t\t\t\"math/rand/v2.Uint64N\"\n\t\t\t\t\"math/rand/v2.UintN\"\n\n\t\t\t\t\"(*math/rand/v2.Rand).Int32N\"\n\t\t\t\t\"(*math/rand/v2.Rand).Int64N\"\n\t\t\t\t\"(*math/rand/v2.Rand).IntN\"\n\t\t\t\t\"(*math/rand/v2.Rand).Uint32N\"\n\t\t\t\t\"(*math/rand/v2.Rand).Uint64N\"\n\t\t\t\t\"(*math/rand/v2.Rand).UintN\"))\n\t\t[(IntegerLiteral \"1\")])`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, ineffectiveRandIntQ) {\n\t\treport.Report(pass, node,\n\t\t\tfmt.Sprintf(\"%s(n) generates a random value 0 <= x < n; that is, the generated values don't include n; %s therefore always returns 0\",\n\t\t\t\tm.State[\"name\"], report.Render(pass, node)))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4030/sa4030_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4030\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4030/testdata/go1.0/CheckIneffectiveRandInt/CheckIneffectiveRandInt.go",
    "content": "package pkg\n\nimport \"math/rand\"\n\nfunc fn() {\n\ttype T struct {\n\t\trng rand.Rand\n\t}\n\n\t_ = rand.Intn(1)   //@ diag(re`rand\\.Intn\\(n\\) generates.+rand\\.Intn\\(1\\) therefore`)\n\t_ = rand.Int63n(1) //@ diag(re`rand\\.Int63n\\(n\\) generates.+rand\\.Int63n\\(1\\) therefore`)\n\tvar t T\n\t_ = t.rng.Intn(1) //@ diag(re`\\(\\*math/rand\\.Rand\\)\\.Intn\\(n\\) generates.+t\\.rng\\.Intn\\(1\\) therefore`)\n\n\t_ = rand.Intn(2)\n}\n"
  },
  {
    "path": "staticcheck/sa4030/testdata/go1.22/CheckIneffectiveRandInt/CheckIneffectiveRandInt.go",
    "content": "package pkg\n\nimport \"math/rand/v2\"\n\nfunc fn() {\n\ttype T struct {\n\t\trng rand.Rand\n\t}\n\n\t_ = rand.IntN(1)   //@ diag(re`rand/v2\\.IntN\\(n\\) generates.+rand\\.IntN\\(1\\) therefore`)\n\t_ = rand.Int64N(1) //@ diag(re`rand/v2\\.Int64N\\(n\\) generates.+rand\\.Int64N\\(1\\) therefore`)\n\t_ = rand.N(1)      //@ diag(re`rand/v2\\.N\\(n\\) generates.+rand\\.N\\(1\\) therefore`)\n\tvar t T\n\t_ = t.rng.IntN(1) //@ diag(re`\\(\\*math/rand/v2\\.Rand\\)\\.IntN\\(n\\) generates.+t\\.rng\\.IntN\\(1\\) therefore`)\n\n\t_ = rand.IntN(2)\n}\n"
  },
  {
    "path": "staticcheck/sa4031/sa4031.go",
    "content": "package sa4031\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"sort\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/pattern\"\n\t\"honnef.co/go/tools/staticcheck/sa4022\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4031\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer, inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Checking never-nil value against nil`,\n\t\tSince:    \"2022.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar allocationNilCheckQ = pattern.MustParse(`(IfStmt _ cond@(BinaryExpr lhs op@(Or \"==\" \"!=\") (Builtin \"nil\")) _ _)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tirpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg\n\n\tvar path []ast.Node\n\tfn := func(node ast.Node, stack []ast.Node) {\n\t\tm, ok := code.Match(pass, allocationNilCheckQ, node)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tcond := m.State[\"cond\"].(ast.Node)\n\t\tif _, ok := code.Match(pass, sa4022.CheckAddressIsNilQ, cond); ok {\n\t\t\t// Don't duplicate diagnostics reported by SA4022\n\t\t\treturn\n\t\t}\n\t\tlhs := m.State[\"lhs\"].(ast.Expr)\n\t\tpath = path[:0]\n\t\tfor i := len(stack) - 1; i >= 0; i-- {\n\t\t\tpath = append(path, stack[i])\n\t\t}\n\t\tirfn := ir.EnclosingFunction(irpkg, path)\n\t\tif irfn == nil {\n\t\t\t// For example for functions named \"_\", because we don't generate IR for them.\n\t\t\treturn\n\t\t}\n\t\tv, isAddr := irfn.ValueForExpr(lhs)\n\t\tif isAddr {\n\t\t\treturn\n\t\t}\n\n\t\tseen := map[ir.Value]struct{}{}\n\t\tvar values []ir.Value\n\t\tvar neverNil func(v ir.Value, track bool) bool\n\t\tneverNil = func(v ir.Value, track bool) bool {\n\t\t\tif _, ok := seen[v]; ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tseen[v] = struct{}{}\n\t\t\tswitch v := v.(type) {\n\t\t\tcase *ir.MakeClosure, *ir.Function:\n\t\t\t\tif track {\n\t\t\t\t\tvalues = append(values, v)\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\tcase *ir.MakeChan, *ir.MakeMap, *ir.MakeSlice, *ir.Alloc:\n\t\t\t\tif track {\n\t\t\t\t\tvalues = append(values, v)\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\tcase *ir.Slice:\n\t\t\t\tif track {\n\t\t\t\t\tvalues = append(values, v)\n\t\t\t\t}\n\t\t\t\treturn neverNil(v.X, false)\n\t\t\tcase *ir.FieldAddr:\n\t\t\t\tif track {\n\t\t\t\t\tvalues = append(values, v)\n\t\t\t\t}\n\t\t\t\treturn neverNil(v.X, false)\n\t\t\tcase *ir.Sigma:\n\t\t\t\treturn neverNil(v.X, true)\n\t\t\tcase *ir.Phi:\n\t\t\t\tfor _, e := range v.Edges {\n\t\t\t\t\tif !neverNil(e, true) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\tif !neverNil(v, true) {\n\t\t\treturn\n\t\t}\n\n\t\tvar qualifier string\n\t\tif op := m.State[\"op\"].(token.Token); op == token.EQL {\n\t\t\tqualifier = \"never\"\n\t\t} else {\n\t\t\tqualifier = \"always\"\n\t\t}\n\t\tfallback := fmt.Sprintf(\"this nil check is %s true\", qualifier)\n\n\t\tsort.Slice(values, func(i, j int) bool { return values[i].Pos() < values[j].Pos() })\n\n\t\tif ident, ok := m.State[\"lhs\"].(*ast.Ident); ok {\n\t\t\tif _, ok := pass.TypesInfo.ObjectOf(ident).(*types.Var); ok {\n\t\t\t\tvar opts []report.Option\n\t\t\t\tif v.Parent() == irfn {\n\t\t\t\t\tif len(values) == 1 {\n\t\t\t\t\t\topts = append(opts, report.Related(values[0], fmt.Sprintf(\"this is the value of %s\", ident.Name)))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor _, vv := range values {\n\t\t\t\t\t\t\topts = append(opts, report.Related(vv, fmt.Sprintf(\"this is one of the value of %s\", ident.Name)))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tswitch v.(type) {\n\t\t\t\tcase *ir.MakeClosure, *ir.Function:\n\t\t\t\t\treport.Report(pass, cond, \"the checked variable contains a function and is never nil; did you mean to call it?\", opts...)\n\t\t\t\tdefault:\n\t\t\t\t\treport.Report(pass, cond, fallback, opts...)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif _, ok := v.(*ir.Function); ok {\n\t\t\t\t\treport.Report(pass, cond, \"functions are never nil; did you mean to call it?\")\n\t\t\t\t} else {\n\t\t\t\t\treport.Report(pass, cond, fallback)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif _, ok := v.(*ir.Function); ok {\n\t\t\t\treport.Report(pass, cond, \"functions are never nil; did you mean to call it?\")\n\t\t\t} else {\n\t\t\t\treport.Report(pass, cond, fallback)\n\t\t\t}\n\t\t}\n\t}\n\tcode.PreorderStack(pass, fn, (*ast.IfStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa4031/sa4031_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4031\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4031/testdata/go1.0/CheckAllocationNilCheck/CheckAllocationNilCheck.go",
    "content": "package pkg\n\ntype T struct {\n\tField func()\n}\n\nfunc (T) Method() {}\n\nfunc gen() *int { return nil }\n\nfunc fn1() {\n\tvar a *int\n\tb := new(int)\n\tc := make([]byte, 0)\n\tvar t T\n\tvar pt *T\n\td := t.Field\n\te := t.Method\n\tf := &t.Field\n\tg := fn1\n\th := &T{}\n\ti := gen()\n\tj := func() {}\n\tk := make(map[string]int)\n\tvar slice []byte\n\tl := slice[:0]\n\tvar m []byte\n\tif true {\n\t\tm = []byte{}\n\t} else {\n\t\tm = []byte{}\n\t}\n\tn := m[:0]\n\to := &pt.Field\n\n\tif a != nil {\n\t}\n\tif b != nil { //@ diag(`always true`)\n\t}\n\tif b == nil { //@ diag(`never true`)\n\t}\n\tif c != nil { //@ diag(`always true`)\n\t}\n\tif d != nil { // field value could be anything\n\t}\n\tif e != nil { //@ diag(`contains a function`)\n\t}\n\tif f != nil { //@ diag(`always true`)\n\t}\n\tif g != nil { //@ diag(`contains a function`)\n\t}\n\tif h != nil { //@ diag(`always true`)\n\t}\n\tif &a != nil { // already flagged by SA4022\n\t}\n\tif (&T{}).Method != nil { //@ diag(`always true`)\n\t}\n\tif (&T{}) != nil { // already flagged by SA4022\n\t}\n\tif i != nil { // just a function return value\n\t}\n\tif fn1 != nil { //@ diag(`functions are never nil`)\n\t}\n\tif j != nil { //@ diag(`contains a function`)\n\t}\n\tif k != nil { //@ diag(`always true`)\n\t}\n\tif l != nil { // slicing a nil slice yields nil\n\t}\n\tif m != nil { //@ diag(`always true`)\n\t}\n\tif n != nil { //@ diag(`always true`)\n\t}\n\tif o != nil { // in &pt.Field, pt might be nil\n\t}\n}\n\nfunc fn2() {\n\tx := new(int)\n\tif true {\n\t\tx = nil\n\t}\n\tif x != nil {\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4032/sa4032.go",
    "content": "package sa4032\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/build/constraint\"\n\t\"go/constant\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/knowledge\"\n\t\"honnef.co/go/tools/pattern\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA4032\",\n\t\tRun:      CheckImpossibleGOOSGOARCH,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Comparing \\'runtime.GOOS\\' or \\'runtime.GOARCH\\' against impossible value`,\n\t\tSince:    \"2024.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tgoosComparisonQ   = pattern.MustParse(`(BinaryExpr (Symbol \"runtime.GOOS\") op@(Or \"==\" \"!=\") lit@(BasicLit \"STRING\" _))`)\n\tgoarchComparisonQ = pattern.MustParse(`(BinaryExpr (Symbol \"runtime.GOARCH\") op@(Or \"==\" \"!=\") lit@(BasicLit \"STRING\" _))`)\n)\n\nfunc CheckImpossibleGOOSGOARCH(pass *analysis.Pass) (any, error) {\n\t// TODO(dh): validate GOOS and GOARCH together. that is,\n\t// given '(linux && amd64) || (windows && mips)',\n\t// flag 'if runtime.GOOS == \"linux\" && runtime.GOARCH == \"mips\"'\n\t//\n\t// We can't use our IR for the control flow graph, because go/types constant folds constant comparisons, so\n\t// 'runtime.GOOS == \"windows\"' will just become 'false'. We can't use the AST-based CFG builder from x/tools,\n\t// because it doesn't model branch conditions.\n\n\tif !code.CouldMatchAny(pass, goarchComparisonQ, goosComparisonQ) {\n\t\treturn nil, nil\n\t}\n\n\tfor _, f := range pass.Files {\n\t\texpr, ok := code.BuildConstraints(pass, f)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tast.Inspect(f, func(node ast.Node) bool {\n\t\t\tif m, ok := code.Match(pass, goosComparisonQ, node); ok {\n\t\t\t\ttv := pass.TypesInfo.Types[m.State[\"lit\"].(ast.Expr)]\n\t\t\t\tgoos := constant.StringVal(tv.Value)\n\n\t\t\t\tif _, ok := knowledge.KnownGOOS[goos]; !ok {\n\t\t\t\t\t// Don't try to reason about GOOS values we don't know about. Maybe the user is using a newer\n\t\t\t\t\t// version of Go that supports a new target, or maybe they run a fork of Go.\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tsat, ok := validateGOOSComparison(expr, goos)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif !sat {\n\t\t\t\t\t// Note that we do not have to worry about constraints that can never be satisfied, such as 'linux\n\t\t\t\t\t// && windows'. Packages with such files will not be passed to Staticcheck in the first place,\n\t\t\t\t\t// precisely because the constraints aren't satisfiable.\n\t\t\t\t\treport.Report(pass, node,\n\t\t\t\t\t\tfmt.Sprintf(\"due to the file's build constraints, runtime.GOOS will never equal %q\", goos))\n\t\t\t\t}\n\t\t\t} else if m, ok := code.Match(pass, goarchComparisonQ, node); ok {\n\t\t\t\ttv := pass.TypesInfo.Types[m.State[\"lit\"].(ast.Expr)]\n\t\t\t\tgoarch := constant.StringVal(tv.Value)\n\n\t\t\t\tif _, ok := knowledge.KnownGOARCH[goarch]; !ok {\n\t\t\t\t\t// Don't try to reason about GOARCH values we don't know about. Maybe the user is using a newer\n\t\t\t\t\t// version of Go that supports a new target, or maybe they run a fork of Go.\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tsat, ok := validateGOARCHComparison(expr, goarch)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif !sat {\n\t\t\t\t\t// Note that we do not have to worry about constraints that can never be satisfied, such as 'amd64\n\t\t\t\t\t// && mips'. Packages with such files will not be passed to Staticcheck in the first place,\n\t\t\t\t\t// precisely because the constraints aren't satisfiable.\n\t\t\t\t\treport.Report(pass, node,\n\t\t\t\t\t\tfmt.Sprintf(\"due to the file's build constraints, runtime.GOARCH will never equal %q\", goarch))\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}\n\n\treturn nil, nil\n}\nfunc validateGOOSComparison(expr constraint.Expr, goos string) (sat bool, didCheck bool) {\n\tmatchGoosTag := func(tag string, goos string) (ok bool, goosTag bool) {\n\t\tswitch tag {\n\t\tcase \"aix\",\n\t\t\t\"android\",\n\t\t\t\"dragonfly\",\n\t\t\t\"freebsd\",\n\t\t\t\"hurd\",\n\t\t\t\"illumos\",\n\t\t\t\"ios\",\n\t\t\t\"js\",\n\t\t\t\"netbsd\",\n\t\t\t\"openbsd\",\n\t\t\t\"plan9\",\n\t\t\t\"wasip1\",\n\t\t\t\"windows\":\n\t\t\treturn goos == tag, true\n\t\tcase \"darwin\":\n\t\t\treturn (goos == \"darwin\" || goos == \"ios\"), true\n\t\tcase \"linux\":\n\t\t\treturn (goos == \"linux\" || goos == \"android\"), true\n\t\tcase \"solaris\":\n\t\t\treturn (goos == \"solaris\" || goos == \"illumos\"), true\n\t\tcase \"unix\":\n\t\t\treturn (goos == \"aix\" ||\n\t\t\t\tgoos == \"android\" ||\n\t\t\t\tgoos == \"darwin\" ||\n\t\t\t\tgoos == \"dragonfly\" ||\n\t\t\t\tgoos == \"freebsd\" ||\n\t\t\t\tgoos == \"hurd\" ||\n\t\t\t\tgoos == \"illumos\" ||\n\t\t\t\tgoos == \"ios\" ||\n\t\t\t\tgoos == \"linux\" ||\n\t\t\t\tgoos == \"netbsd\" ||\n\t\t\t\tgoos == \"openbsd\" ||\n\t\t\t\tgoos == \"solaris\"), true\n\t\tdefault:\n\t\t\treturn false, false\n\t\t}\n\t}\n\n\treturn validateTagComparison(expr, func(tag string) (matched bool, special bool) {\n\t\treturn matchGoosTag(tag, goos)\n\t})\n}\n\nfunc validateGOARCHComparison(expr constraint.Expr, goarch string) (sat bool, didCheck bool) {\n\tmatchGoarchTag := func(tag string, goarch string) (ok bool, goosTag bool) {\n\t\tswitch tag {\n\t\tcase \"386\",\n\t\t\t\"amd64\",\n\t\t\t\"arm\",\n\t\t\t\"arm64\",\n\t\t\t\"loong64\",\n\t\t\t\"mips\",\n\t\t\t\"mipsle\",\n\t\t\t\"mips64\",\n\t\t\t\"mips64le\",\n\t\t\t\"ppc64\",\n\t\t\t\"ppc64le\",\n\t\t\t\"riscv64\",\n\t\t\t\"s390x\",\n\t\t\t\"sparc64\",\n\t\t\t\"wasm\":\n\t\t\treturn goarch == tag, true\n\t\tdefault:\n\t\t\treturn false, false\n\t\t}\n\t}\n\n\treturn validateTagComparison(expr, func(tag string) (matched bool, special bool) {\n\t\treturn matchGoarchTag(tag, goarch)\n\t})\n}\n\nfunc validateTagComparison(expr constraint.Expr, matchSpecialTag func(tag string) (matched bool, special bool)) (sat bool, didCheck bool) {\n\totherTags := map[string]int{}\n\t// Collect all tags that aren't known architecture-based tags\n\tb := expr.Eval(func(tag string) bool {\n\t\tok, special := matchSpecialTag(tag)\n\t\tif !special {\n\t\t\t// Assign an ID to this tag, but only if we haven't seen it before. For the expression 'foo && foo', this\n\t\t\t// callback will be called twice for the 'foo' tag.\n\t\t\tif _, ok := otherTags[tag]; !ok {\n\t\t\t\totherTags[tag] = len(otherTags)\n\t\t\t}\n\t\t}\n\t\treturn ok\n\t})\n\n\tif b || len(otherTags) == 0 {\n\t\t// We're done. Either the formula can be satisfied regardless of the values of non-special tags, if any,\n\t\t// or there aren't any non-special tags and the formula cannot be satisfied.\n\t\treturn b, true\n\t}\n\n\tif len(otherTags) > 10 {\n\t\t// We have to try 2**len(otherTags) combinations of tags. 2**10 is about the worst we're willing to try.\n\t\treturn false, false\n\t}\n\n\t// Try all permutations of otherTags. If any evaluates to true, then the expression is satisfiable.\n\tfor bits := 0; bits < 1<<len(otherTags); bits++ {\n\t\tb := expr.Eval(func(tag string) bool {\n\t\t\tok, special := matchSpecialTag(tag)\n\t\t\tif special {\n\t\t\t\treturn ok\n\t\t\t}\n\t\t\treturn bits&(1<<otherTags[tag]) != 0\n\t\t})\n\t\tif b {\n\t\t\treturn true, true\n\t\t}\n\t}\n\n\treturn false, true\n}\n"
  },
  {
    "path": "staticcheck/sa4032/sa4032_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa4032\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa4032/testdata/go1.0/CheckGOOSComparison/complex.go",
    "content": "//go:build linux || windows\n\npackage pkg\n\nimport \"runtime\"\n\nfunc complex() {\n\tif runtime.GOOS == \"linux\" {\n\t}\n\tif runtime.GOOS == \"android\" {\n\t}\n\tif runtime.GOOS == \"windows\" {\n\t}\n\tif runtime.GOOS == \"darwin\" { //@ diag(`runtime.GOOS will never equal \"darwin\"`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4032/testdata/go1.0/CheckGOOSComparison/f_linux.go",
    "content": "package pkg\n\nimport \"runtime\"\n\nfunc fileLinux() {\n\tif runtime.GOOS == \"windows\" { //@ diag(`runtime.GOOS will never equal \"windows\"`)\n\t}\n\tif runtime.GOOS == \"android\" {\n\t}\n\tif runtime.GOOS == \"linux\" {\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4032/testdata/go1.0/CheckGOOSComparison/f_unix.go",
    "content": "package pkg\n\nimport \"runtime\"\n\nfunc fileUnix() {\n\t// \"unix\" is not a magic file name suffix, so all of these branches are fine\n\tif runtime.GOOS == \"windows\" {\n\t}\n\tif runtime.GOOS == \"android\" {\n\t}\n\tif runtime.GOOS == \"linux\" {\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4032/testdata/go1.0/CheckGOOSComparison/f_windows.go",
    "content": "package pkg\n\nimport \"runtime\"\n\nfunc fileWindows() {\n\tif runtime.GOOS == \"windows\" {\n\t}\n\tif runtime.GOOS == \"linux\" { //@ diag(`runtime.GOOS will never equal \"linux\"`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4032/testdata/go1.0/CheckGOOSComparison/other.go",
    "content": "//go:build !(linux || windows || unix)\n\npackage pkg\n\nimport \"runtime\"\n\nfunc other() {\n\tif runtime.GOOS == \"linux\" { //@ diag(`runtime.GOOS will never equal \"linux\"`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4032/testdata/go1.0/CheckGOOSComparison/tlinux.go",
    "content": "//go:build linux\n\npackage pkg\n\nimport \"runtime\"\n\nfunc linux() {\n\tif runtime.GOOS == \"windows\" { //@ diag(`runtime.GOOS will never equal \"windows\"`)\n\t}\n\tif runtime.GOOS == \"android\" {\n\t}\n\tif runtime.GOOS == \"linux\" {\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4032/testdata/go1.0/CheckGOOSComparison/tunix.go",
    "content": "//go:build unix\n\npackage pkg\n\nimport \"runtime\"\n\nfunc unix() {\n\tif runtime.GOOS == \"windows\" { //@ diag(`runtime.GOOS will never equal \"windows\"`)\n\t}\n\tif runtime.GOOS == \"linux\" {\n\t}\n\tif runtime.GOOS == \"darwin\" {\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4032/testdata/go1.0/CheckGOOSComparison/twindows.go",
    "content": "//go:build windows\n\npackage pkg\n\nimport \"runtime\"\n\nfunc windows() {\n\tif runtime.GOOS == \"windows\" {\n\t}\n\tif runtime.GOOS == \"linux\" { //@ diag(`runtime.GOOS will never equal \"linux\"`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa4032/testdata/go1.0/CheckGOOSComparison/unknown.go",
    "content": "//go:build unix || windows\n\npackage pkg\n\nimport \"runtime\"\n\nfunc unknown() {\n\t// don't flag this, we don't know what DomOS is.\n\tif runtime.GOOS == \"DomOS\" {\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5000/sa5000.go",
    "content": "package sa5000\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA5000\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Assignment to nil map`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tfor _, block := range fn.Blocks {\n\t\t\tfor _, ins := range block.Instrs {\n\t\t\t\tmu, ok := ins.(*ir.MapUpdate)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tc, ok := irutil.Flatten(mu.Map).(*ir.Const)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif c.Value != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treport.Report(pass, mu, \"assignment to nil map\")\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa5000/sa5000_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5000\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5000/testdata/go1.0/CheckNilMaps/CheckNilMaps.go",
    "content": "package pkg\n\nimport \"fmt\"\n\nfunc fn1() {\n\tvar m map[int]int\n\tm[1] = 1 //@ diag(`assignment to nil map`)\n}\n\nfunc fn2(m map[int]int) {\n\tm[1] = 1\n}\n\nfunc fn3() {\n\tv := []int{1, 2, 3}\n\tvar m map[string]int\n\tfor i := range v {\n\t\tm[\"a\"] = i //@ diag(`assignment to nil map`)\n\t}\n\tfmt.Println(m[\"a\"])\n}\n\nfunc fn4() {\n\tm := map[string]int{}\n\tif true {\n\t\tif true {\n\t\t\tm[\"\"] = 0\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5001/sa5001.go",
    "content": "package sa5001\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA5001\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Deferring \\'Close\\' before checking for a possible error`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tblock := node.(*ast.BlockStmt)\n\t\tif len(block.List) < 2 {\n\t\t\treturn\n\t\t}\n\t\tfor i, stmt := range block.List {\n\t\t\tif i == len(block.List)-1 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tassign, ok := stmt.(*ast.AssignStmt)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif len(assign.Rhs) != 1 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif len(assign.Lhs) < 2 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif lhs, ok := assign.Lhs[len(assign.Lhs)-1].(*ast.Ident); ok && lhs.Name == \"_\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcall, ok := assign.Rhs[0].(*ast.CallExpr)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif sig.Results().Len() < 2 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlast := sig.Results().At(sig.Results().Len() - 1)\n\t\t\t// FIXME(dh): check that it's error from universe, not\n\t\t\t// another type of the same name\n\t\t\tif last.Type().String() != \"error\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlhs, ok := assign.Lhs[0].(*ast.Ident)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdef, ok := block.List[i+1].(*ast.DeferStmt)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsel, ok := def.Call.Fun.(*ast.SelectorExpr)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tident, ok := selectorX(sel).(*ast.Ident)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif pass.TypesInfo.ObjectOf(ident) != pass.TypesInfo.ObjectOf(lhs) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif sel.Sel.Name != \"Close\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treport.Report(pass, def, fmt.Sprintf(\"should check error returned from %s() before deferring %s\",\n\t\t\t\treport.Render(pass, call.Fun), report.Render(pass, def.Call)))\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.BlockStmt)(nil))\n\treturn nil, nil\n}\n\nfunc selectorX(sel *ast.SelectorExpr) ast.Node {\n\tswitch x := sel.X.(type) {\n\tcase *ast.SelectorExpr:\n\t\treturn selectorX(x)\n\tdefault:\n\t\treturn x\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5001/sa5001_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5001\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5001/testdata/go1.0/CheckEarlyDefer/CheckEarlyDefer.go",
    "content": "package pkg\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\nfunc fn1() (io.ReadCloser, error) {\n\treturn nil, nil\n}\n\ntype T struct {\n\trc io.ReadCloser\n}\n\ntype T2 struct{}\n\nfunc (T2) create() (io.ReadCloser, error) {\n\treturn nil, nil\n}\n\nfunc fn3() (T, error) {\n\treturn T{}, nil\n}\n\nfunc fn2() {\n\trc, err := fn1()\n\tdefer rc.Close() //@ diag(`should check error returned from fn1() before deferring rc.Close`)\n\tif err != nil {\n\t\tprintln()\n\t}\n\n\trc, _ = fn1()\n\tdefer rc.Close()\n\n\trc, err = fn1()\n\tif err != nil {\n\t\tprintln()\n\t}\n\tdefer rc.Close()\n\n\tt, err := fn3()\n\tdefer t.rc.Close() //@ diag(`should check error returned from fn3() before deferring t.rc.Close`)\n\tif err != nil {\n\t\tprintln()\n\t}\n\n\tfp, err := os.Open(\"path\")\n\tdefer fp.Close() //@ diag(`should check error returned from os.Open() before deferring fp.Close()`)\n\tif err != nil {\n\t\tprintln()\n\t}\n\n\tvar t2 T2\n\trc2, err := t2.create()\n\tdefer rc2.Close() //@ diag(`should check error returned from t2.create() before deferring rc2.Close()`)\n\tif err != nil {\n\t\tprintln()\n\t}\n\n\t// Don't flag this, we're closing a different reader\n\tx, err := fn1()\n\tdefer rc.Close()\n\tif err != nil {\n\t\tprintln()\n\t}\n\t_ = x\n}\n"
  },
  {
    "path": "staticcheck/sa5002/sa5002.go",
    "content": "package sa5002\n\nimport (\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA5002\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `The empty for loop (\\\"for {}\\\") spins and can block the scheduler`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tloop := node.(*ast.ForStmt)\n\t\tif len(loop.Body.List) != 0 || loop.Post != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif loop.Init != nil {\n\t\t\t// TODO(dh): this isn't strictly necessary, it just makes\n\t\t\t// the check easier.\n\t\t\treturn\n\t\t}\n\t\t// An empty loop is bad news in two cases: 1) The loop has no\n\t\t// condition. In that case, it's just a loop that spins\n\t\t// forever and as fast as it can, keeping a core busy. 2) The\n\t\t// loop condition only consists of variable or field reads and\n\t\t// operators on those. The only way those could change their\n\t\t// value is with unsynchronised access, which constitutes a\n\t\t// data race.\n\t\t//\n\t\t// If the condition contains any function calls, its behaviour\n\t\t// is dynamic and the loop might terminate. Similarly for\n\t\t// channel receives.\n\n\t\tif loop.Cond != nil {\n\t\t\tif code.MayHaveSideEffects(pass, loop.Cond, nil) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif ident, ok := loop.Cond.(*ast.Ident); ok {\n\t\t\t\tif k, ok := pass.TypesInfo.ObjectOf(ident).(*types.Const); ok {\n\t\t\t\t\tif !constant.BoolVal(k.Val()) {\n\t\t\t\t\t\t// don't flag `for false {}` loops. They're a debug aid.\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treport.Report(pass, loop, \"loop condition never changes or has a race condition\")\n\t\t}\n\t\treport.Report(pass, loop, \"this loop will spin, using 100% CPU\", report.ShortRange())\n\t}\n\tcode.Preorder(pass, fn, (*ast.ForStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa5002/sa5002_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5002\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5002/testdata/go1.0/CheckInfiniteEmptyLoop/CheckInfiniteEmptyLoop.go",
    "content": "package pkg\n\nfunc fn2() bool { return true }\n\nfunc fn() {\n\tfor { //@ diag(`this loop will spin`)\n\t}\n\n\tfor fn2() {\n\t}\n\n\tfor {\n\t\tbreak\n\t}\n\n\tfor true { //@ diag(`loop condition never changes`), diag(`this loop will spin`)\n\t}\n\n\tx := true\n\tfor x { //@ diag(`loop condition never changes`), diag(`this loop will spin`)\n\t}\n\n\tx = false\n\tfor x { //@ diag(`loop condition never changes`), diag(`this loop will spin`)\n\t}\n\n\tfor false {\n\t}\n\n\tfalse := true\n\tfor false { //@ diag(`loop condition never changes`), diag(`this loop will spin`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5003/sa5003.go",
    "content": "package sa5003\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA5003\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Defers in infinite loops will never execute`,\n\t\tText: `Defers are scoped to the surrounding function, not the surrounding\nblock. In a function that never returns, i.e. one containing an\ninfinite loop, defers will never execute.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tmightExit := false\n\t\tvar defers []ast.Stmt\n\t\tloop := node.(*ast.ForStmt)\n\t\tif loop.Cond != nil {\n\t\t\treturn\n\t\t}\n\t\tfn2 := func(node ast.Node) bool {\n\t\t\tswitch stmt := node.(type) {\n\t\t\tcase *ast.ReturnStmt:\n\t\t\t\tmightExit = true\n\t\t\t\treturn false\n\t\t\tcase *ast.BranchStmt:\n\t\t\t\t// TODO(dominikh): if this sees a break in a switch or\n\t\t\t\t// select, it doesn't check if it breaks the loop or\n\t\t\t\t// just the select/switch. This causes some false\n\t\t\t\t// negatives.\n\t\t\t\tif stmt.Tok == token.BREAK {\n\t\t\t\t\tmightExit = true\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\tcase *ast.DeferStmt:\n\t\t\t\tdefers = append(defers, stmt)\n\t\t\tcase *ast.FuncLit:\n\t\t\t\t// Don't look into function bodies\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t\tast.Inspect(loop.Body, fn2)\n\t\tif mightExit {\n\t\t\treturn\n\t\t}\n\t\tfor _, stmt := range defers {\n\t\t\treport.Report(pass, stmt, \"defers in this infinite loop will never run\")\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.ForStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa5003/sa5003_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5003\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5003/testdata/go1.0/CheckDeferInInfiniteLoop/CheckDeferInInfiniteLoop.go",
    "content": "package pkg\n\nfunc fn() {\n\tfor {\n\t\tdefer println() //@ diag(`will never run`)\n\t}\n\tfor {\n\t\tdefer println() //@ diag(`will never run`)\n\t\tgo func() {\n\t\t\treturn\n\t\t}()\n\t}\n\tfor {\n\t\tdefer println()\n\t\treturn\n\t}\n\tfor false {\n\t\tdefer println()\n\t}\n\tfor {\n\t\tdefer println()\n\t\tbreak\n\t}\n\tfor {\n\t\tdefer println()\n\t\tif true {\n\t\t\tbreak\n\t\t}\n\t}\n\tfor {\n\t\tdefer println()\n\t\t// Right now, we treat every break the same. We could analyse\n\t\t// this further and see, that the break doesn't break out of\n\t\t// the outer loop.\n\t\tfor {\n\t\t\tbreak\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5004/sa5004.go",
    "content": "package sa5004\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA5004\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `\\\"for { select { ...\\\" with an empty default branch spins`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar query = pattern.MustParse(`(ForStmt nil nil nil (SelectStmt body))`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, m := range code.Matches(pass, query) {\n\t\tfor _, c := range m.State[\"body\"].([]ast.Stmt) {\n\t\t\t// FIXME this leaves behind an empty line, and possibly\n\t\t\t// comments in the default branch. We can't easily fix\n\t\t\t// either.\n\t\t\tif comm, ok := c.(*ast.CommClause); ok && comm.Comm == nil && len(comm.Body) == 0 {\n\t\t\t\treport.Report(pass, comm,\n\t\t\t\t\t\"should not have an empty default case in a for+select loop; the loop will spin\",\n\t\t\t\t\treport.Fixes(edit.Fix(\"Remove empty default branch\", edit.Delete(comm))))\n\t\t\t\t// there can only be one default case\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa5004/sa5004_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5004\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5004/testdata/go1.0/CheckLoopEmptyDefault/CheckLoopEmptyDefault.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar ch chan int\n\tselect {\n\tcase <-ch:\n\tdefault:\n\t}\n\n\tfor {\n\t\tselect {\n\t\tcase <-ch:\n\t\tdefault: //@ diag(`should not have an empty default case`)\n\t\t}\n\t}\n\n\tfor {\n\t\tselect {\n\t\tcase <-ch:\n\t\tdefault:\n\t\t\tprintln(\"foo\")\n\t\t}\n\t}\n\n\tfor {\n\t\tselect {\n\t\tcase <-ch:\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5004/testdata/go1.0/CheckLoopEmptyDefault/CheckLoopEmptyDefault.go.golden",
    "content": "package pkg\n\nfunc fn() {\n\tvar ch chan int\n\tselect {\n\tcase <-ch:\n\tdefault:\n\t}\n\n\tfor {\n\t\tselect {\n\t\tcase <-ch:\n\t\t\t//@ diag(`should not have an empty default case`)\n\t\t}\n\t}\n\n\tfor {\n\t\tselect {\n\t\tcase <-ch:\n\t\tdefault:\n\t\t\tprintln(\"foo\")\n\t\t}\n\t}\n\n\tfor {\n\t\tselect {\n\t\tcase <-ch:\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5005/sa5005.go",
    "content": "package sa5005\n\nimport (\n\t\"fmt\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA5005\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `The finalizer references the finalized object, preventing garbage collection`,\n\t\tText: `A finalizer is a function associated with an object that runs when the\ngarbage collector is ready to collect said object, that is when the\nobject is no longer referenced by anything.\n\nIf the finalizer references the object, however, it will always remain\nas the final reference to that object, preventing the garbage\ncollector from collecting the object. The finalizer will never run,\nand the object will never be collected, leading to a memory leak. That\nis why the finalizer should instead use its first argument to operate\non the object. That way, the number of references can temporarily go\nto zero before the object is being passed to the finalizer.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tcb := func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) {\n\t\tif callee.RelString(nil) != \"runtime.SetFinalizer\" {\n\t\t\treturn\n\t\t}\n\t\targ0 := site.Common().Args[knowledge.Arg(\"runtime.SetFinalizer.obj\")]\n\t\tif iface, ok := arg0.(*ir.MakeInterface); ok {\n\t\t\targ0 = iface.X\n\t\t}\n\t\tload, ok := arg0.(*ir.Load)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tv, ok := load.X.(*ir.Alloc)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\targ1 := site.Common().Args[knowledge.Arg(\"runtime.SetFinalizer.finalizer\")]\n\t\tif iface, ok := arg1.(*ir.MakeInterface); ok {\n\t\t\targ1 = iface.X\n\t\t}\n\t\tmc, ok := arg1.(*ir.MakeClosure)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tfor _, b := range mc.Bindings {\n\t\t\tif b == v {\n\t\t\t\tpos := report.DisplayPosition(pass.Fset, mc.Fn.Pos())\n\t\t\t\treport.Report(pass, site, fmt.Sprintf(\"the finalizer closes over the object, preventing the finalizer from ever running (at %s)\", pos))\n\t\t\t}\n\t\t}\n\t}\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\teachCall(fn, cb)\n\t}\n\treturn nil, nil\n}\n\nfunc eachCall(fn *ir.Function, cb func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function)) {\n\tfor _, b := range fn.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tif site, ok := instr.(ir.CallInstruction); ok {\n\t\t\t\tif g := site.Common().StaticCallee(); g != nil {\n\t\t\t\t\tcb(fn, site, g)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5005/sa5005_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5005\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5005/testdata/go1.0/CheckCyclicFinalizer/CheckCyclicFinalizer.go",
    "content": "package pkg\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n)\n\nfunc fn() {\n\tvar x *int\n\tfoo := func(y *int) { fmt.Println(x) }\n\truntime.SetFinalizer(x, foo) //@ diag(re`the finalizer closes over the object, preventing the finalizer from ever running \\(at .+:10:9`)\n\truntime.SetFinalizer(x, nil)\n\truntime.SetFinalizer(x, func(_ *int) { //@ diag(re`the finalizer closes over the object, preventing the finalizer from ever running \\(at .+:13:26`)\n\t\tfmt.Println(x)\n\t})\n\n\tfoo = func(y *int) { fmt.Println(y) }\n\truntime.SetFinalizer(x, foo)\n\truntime.SetFinalizer(x, func(y *int) {\n\t\tfmt.Println(y)\n\t})\n}\n"
  },
  {
    "path": "staticcheck/sa5007/sa5007.go",
    "content": "package sa5007\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA5007\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Infinite recursive call`,\n\t\tText: `A function that calls itself recursively needs to have an exit\ncondition. Otherwise it will recurse forever, until the system runs\nout of memory.\n\nThis issue can be caused by simple bugs such as forgetting to add an\nexit condition. It can also happen \"on purpose\". Some languages have\ntail call optimization which makes certain infinite recursive calls\nsafe to use. Go, however, does not implement TCO, and as such a loop\nshould be used instead.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\teachCall(fn, func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) {\n\t\t\tif callee != fn {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif _, ok := site.(*ir.Go); ok {\n\t\t\t\t// Recursively spawning goroutines doesn't consume\n\t\t\t\t// stack space infinitely, so don't flag it.\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tblock := site.Block()\n\t\t\tfor _, b := range fn.Blocks {\n\t\t\t\tif block.Dominates(b) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif len(b.Instrs) == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif _, ok := b.Control().(*ir.Return); ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\treport.Report(pass, site, \"infinite recursive call\")\n\t\t})\n\t}\n\treturn nil, nil\n}\n\nfunc eachCall(fn *ir.Function, cb func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function)) {\n\tfor _, b := range fn.Blocks {\n\t\tfor _, instr := range b.Instrs {\n\t\t\tif site, ok := instr.(ir.CallInstruction); ok {\n\t\t\t\tif g := site.Common().StaticCallee(); g != nil {\n\t\t\t\t\tcb(fn, site, g)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5007/sa5007_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5007\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5007/testdata/go1.0/CheckInfiniteRecursion/CheckInfiniteRecursion.go",
    "content": "package pkg\n\nfunc fn1(x int) bool {\n\tprintln(x)\n\treturn fn1(x + 1) //@ diag(`infinite recursive call`)\n\treturn true\n}\n\nfunc fn2(x int) bool {\n\tprintln(x)\n\tif x > 10 {\n\t\treturn true\n\t}\n\treturn fn2(x + 1)\n}\n\nfunc fn3(x int) bool {\n\tprintln(x)\n\tif x > 10 {\n\t\tgoto l1\n\t}\n\treturn fn3(x + 1)\nl1:\n\tprintln(x)\n\treturn true\n}\n\nfunc fn4(p *int, n int) {\n\tif n == 0 {\n\t\treturn\n\t}\n\tx := 0\n\tfn4(&x, n-1)\n\tif x != n {\n\t\tpanic(\"stack is corrupted\")\n\t}\n}\n\nfunc fn5(p *int, n int) {\n\tx := 0\n\tfn5(&x, n-1) //@ diag(`infinite recursive call`)\n\tif x != n {\n\t\tpanic(\"stack is corrupted\")\n\t}\n}\n\nfunc fn6() {\n\tgo fn6()\n}\n\ntype T struct {\n\tn int\n}\n\nfunc (t T) Fn1() {\n\tt.Fn1() //@ diag(`infinite recursive call`)\n}\n\nfunc (t T) Fn2() {\n\tx := T{}\n\tx.Fn2() //@ diag(`infinite recursive call`)\n}\n\nfunc (t T) Fn3() {\n\tif t.n == 0 {\n\t\treturn\n\t}\n\tt.Fn1()\n}\n"
  },
  {
    "path": "staticcheck/sa5008/jsonv2.go",
    "content": "// Copyright 2021 The Go Authors. All rights reserved.\n\n// This file is a modified copy of Go's encoding/json/v2/field.go\n\npackage sa5008\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nfunc validateJSONTag(pass *analysis.Pass, field *ast.Field, tag string) {\n\thasTag := tag != \"\"\n\ttagOrig := tag\n\n\t// Check whether this field is explicitly ignored.\n\tif tag == \"-\" {\n\t\treturn\n\t}\n\n\t// Check whether this field is unexported and not embedded,\n\t// which Go reflection cannot mutate for the sake of serialization.\n\t//\n\t// An embedded field of an unexported type is still capable of\n\t// forwarding exported fields, which may be JSON serialized.\n\t// This technically operates on the edge of what is permissible by\n\t// the Go language, but the most recent decision is to permit this.\n\t//\n\t// See https://go.dev/issue/24153 and https://go.dev/issue/32772.\n\tanonymous := len(field.Names) == 0\n\tif !anonymous && !field.Names[0].IsExported() {\n\t\t// Tag options specified on an unexported field suggests user error.\n\t\tif hasTag {\n\t\t\treport.Report(pass, field.Tag,\n\t\t\t\tfmt.Sprintf(\"unexported struct field cannot have non-ignored `json:%q` tag\", tag))\n\t\t}\n\t\treturn\n\t}\n\n\tif len(tag) > 0 && !strings.HasPrefix(tag, \",\") {\n\t\t// For better compatibility with v1, accept almost any unescaped name.\n\t\tn := len(tag) - len(strings.TrimLeftFunc(tag, func(r rune) bool {\n\t\t\treturn !strings.ContainsRune(\",\\\\'\\\"`\", r) // reserve comma, backslash, and quotes\n\t\t}))\n\t\tname := tag[:n]\n\n\t\t// If the next character is not a comma, then the name is either\n\t\t// malformed (if n > 0) or a single-quoted name.\n\t\t// In either case, call consumeTagOption to handle it further.\n\t\tvar err error\n\t\tif !strings.HasPrefix(tag[n:], \",\") && len(name) != len(tag) {\n\t\t\tname, n, err = consumeTagOption(tag)\n\t\t\tif err != nil {\n\t\t\t\treport.Report(pass, field.Tag, fmt.Sprintf(\"malformed `json` tag: %v\", err))\n\t\t\t}\n\t\t}\n\t\tif !utf8.ValidString(name) {\n\t\t\treport.Report(pass, field.Tag,\n\t\t\t\tfmt.Sprintf(\"invalid UTF-8 in JSON object name %q\", name))\n\t\t\tname = string([]rune(name)) // replace invalid UTF-8 with utf8.RuneError\n\t\t}\n\t\tif name == \"-\" && tag[0] == '-' {\n\t\t\t// TODO(dh): offer automatic fix\n\t\t\treport.Report(pass, field.Tag,\n\t\t\t\tfmt.Sprintf(\"should encoding/json ignore this field or name it \\\"-\\\"? Either use `json:\\\"-\\\"` to ignore the field or use `json:\\\"'-'%s` to specify %q as the name\",\n\t\t\t\t\tstrings.TrimPrefix(strconv.Quote(tagOrig), `\"-`), name))\n\t\t}\n\t\ttag = tag[n:]\n\t}\n\n\t// Handle any additional tag options (if any).\n\tvar wasFormat bool\n\tseenOpts := make(map[string]bool)\n\tfor len(tag) > 0 {\n\t\t// Consume comma delimiter.\n\t\tif tag[0] != ',' {\n\t\t\treport.Report(pass, field.Tag,\n\t\t\t\tfmt.Sprintf(\"malformed `json` tag: invalid character %q before next option (expecting ',')\",\n\t\t\t\t\ttag[0]))\n\t\t} else {\n\t\t\ttag = tag[len(\",\"):]\n\t\t\tif len(tag) == 0 {\n\t\t\t\treport.Report(pass, field.Tag, \"malformed `json` tag: invalid trailing ',' character\")\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\t// Consume and process the tag option.\n\t\topt, n, err := consumeTagOption(tag)\n\t\tif err != nil {\n\t\t\treport.Report(pass, field.Tag, fmt.Sprintf(\"malformed `json` tag: %v\", err))\n\t\t}\n\t\trawOpt := tag[:n]\n\t\ttag = tag[n:]\n\t\tswitch {\n\t\tcase wasFormat:\n\t\t\treport.Report(pass, field.Tag, \"`format` tag option was not specified last\")\n\t\tcase strings.HasPrefix(rawOpt, \"'\") && strings.TrimFunc(opt, isLetterOrDigit) == \"\":\n\t\t\t// TODO(dh): offer automatic fix\n\t\t\treport.Report(pass, field.Tag,\n\t\t\t\tfmt.Sprintf(\"unnecessarily quoted appearance of `%s` tag option; specify `%s` instead\", rawOpt, opt))\n\t\t}\n\t\tswitch opt {\n\t\tcase \"case\":\n\t\t\tif !strings.HasPrefix(tag, \":\") {\n\t\t\t\t// TODO(dh): offer automatic fix\n\t\t\t\treport.Report(pass, field.Tag,\n\t\t\t\t\t\"missing value for `case` tag option; specify `case:ignore` or `case:strict` instead\")\n\t\t\t\tbreak\n\t\t\t}\n\t\t\ttag = tag[len(\":\"):]\n\t\t\topt, n, err := consumeTagOption(tag)\n\t\t\tif err != nil {\n\t\t\t\treport.Report(pass, field.Tag,\n\t\t\t\t\tfmt.Sprintf(\"malformed value for `case` tag option: %v\", err))\n\t\t\t\tbreak\n\t\t\t}\n\t\t\trawOpt := tag[:n]\n\t\t\ttag = tag[n:]\n\t\t\tif strings.HasPrefix(rawOpt, \"'\") {\n\t\t\t\t// TODO(dh): offer automatic fix\n\t\t\t\treport.Report(pass, field.Tag,\n\t\t\t\t\tfmt.Sprintf(\"unnecessarily quoted appearance of `case:%s` tag option; specify `case:%s` instead\",\n\t\t\t\t\t\trawOpt, opt))\n\t\t\t}\n\t\t\tswitch opt {\n\t\t\tcase \"ignore\":\n\t\t\tcase \"strict\":\n\t\t\tdefault:\n\t\t\t\treport.Report(pass, field.Tag,\n\t\t\t\t\tfmt.Sprintf(\"invalid appearance of unknown `case:%s` tag value\", rawOpt))\n\t\t\t}\n\t\tcase \"inline\":\n\t\tcase \"unknown\":\n\t\tcase \"omitzero\":\n\t\tcase \"omitempty\":\n\t\tcase \"string\":\n\t\t\tconst msg = \"invalid appearance of `string` tag option; it is only intended for fields of numeric types or pointers to those\"\n\t\t\ttset := typeutil.NewTypeSet(pass.TypesInfo.TypeOf(field.Type))\n\t\t\tif len(tset.Terms) == 0 {\n\t\t\t\t// TODO(dh): improve message, call out the use of type parameters\n\t\t\t\treport.Report(pass, field.Tag, msg)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor _, term := range tset.Terms {\n\t\t\t\tT := typeutil.Dereference(term.Type().Underlying())\n\t\t\t\tfor _, term2 := range typeutil.NewTypeSet(T).Terms {\n\t\t\t\t\tbasic, ok := term2.Type().Underlying().(*types.Basic)\n\t\t\t\t\t// We accept bools and strings because v1 of encoding/json\n\t\t\t\t\t// supports those. We don't mention that in the message,\n\t\t\t\t\t// however, because their support is accidental, and v2\n\t\t\t\t\t// doesn't support it.\n\t\t\t\t\tif !ok || (basic.Info()&(types.IsBoolean|types.IsInteger|types.IsFloat|types.IsString)) == 0 {\n\t\t\t\t\t\t// TODO(dh): improve message, show how we arrived at the type\n\t\t\t\t\t\treport.Report(pass, field.Tag, msg)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tcase \"format\":\n\t\t\tif !strings.HasPrefix(tag, \":\") {\n\t\t\t\treport.Report(pass, field.Tag, \"missing value for `format` tag option\")\n\t\t\t\tbreak\n\t\t\t}\n\t\t\ttag = tag[len(\":\"):]\n\t\t\t_, n, err := consumeTagOption(tag)\n\t\t\tif err != nil {\n\t\t\t\treport.Report(pass, field.Tag,\n\t\t\t\t\tfmt.Sprintf(\"malformed value for `format` tag option: %v\", err))\n\t\t\t\tbreak\n\t\t\t}\n\t\t\ttag = tag[n:]\n\t\t\twasFormat = true\n\t\tdefault:\n\t\t\t// Reject keys that resemble one of the supported options.\n\t\t\t// This catches invalid mutants such as \"omitEmpty\" or \"omit_empty\".\n\t\t\tnormOpt := strings.ReplaceAll(strings.ToLower(opt), \"_\", \"\")\n\t\t\tswitch normOpt {\n\t\t\tcase \"case\", \"inline\", \"unknown\", \"omitzero\", \"omitempty\", \"string\", \"format\":\n\t\t\t\treport.Report(pass, field.Tag,\n\t\t\t\t\tfmt.Sprintf(\"invalid appearance of `%s` tag option; specify `%s` instead\",\n\t\t\t\t\t\topt, normOpt))\n\t\t\tdefault:\n\t\t\t\treport.Report(pass, field.Tag,\n\t\t\t\t\tfmt.Sprintf(\"invalid appearance of unknown `%s` tag option\", opt))\n\t\t\t}\n\t\t}\n\n\t\t// Reject duplicates.\n\t\tif seenOpts[opt] {\n\t\t\treport.Report(pass, field.Tag,\n\t\t\t\tfmt.Sprintf(\"duplicate appearance of `%s` tag option\", rawOpt))\n\t\t}\n\t\tseenOpts[opt] = true\n\t}\n\n\tif seenOpts[\"inline\"] && seenOpts[\"unknown\"] {\n\t\treport.Report(pass, field.Tag,\n\t\t\t\"field cannot have both `inline` and `unknown` specified\")\n\t}\n\n\t// TODO(dh): implement more restrictions for types of inlined and unknown\n\t// fields, including recursive restrictions:\n\t//\n\t// - Go struct field %s cannot have any options other than `inline` or `unknown` specified\n\t// - inlined Go struct field %s of type %s with `unknown` tag must be a Go map of string key or a jsontext.Value\n\t// - inlined Go struct field %s is not exported\n\t// - inlined map field %s of type %s must have a string key that does not implement marshal or unmarshal methods\n\t// - inlined Go struct field %s of type %s must be a Go struct, Go map of string key, or jsontext.Value\n}\n\n// consumeTagOption consumes the next option,\n// which is either a Go identifier or a single-quoted string.\n// If the next option is invalid, it returns all of in until the next comma,\n// and reports an error.\nfunc consumeTagOption(in string) (string, int, error) {\n\t// For legacy compatibility with v1, assume options are comma-separated.\n\ti := strings.IndexByte(in, ',')\n\tif i < 0 {\n\t\ti = len(in)\n\t}\n\n\tswitch r, _ := utf8.DecodeRuneInString(in); {\n\t// Option as a Go identifier.\n\tcase r == '_' || unicode.IsLetter(r):\n\t\tn := len(in) - len(strings.TrimLeftFunc(in, isLetterOrDigit))\n\t\treturn in[:n], n, nil\n\t// Option as a single-quoted string.\n\tcase r == '\\'':\n\t\t// The grammar is nearly identical to a double-quoted Go string literal,\n\t\t// but uses single quotes as the terminators. The reason for a custom\n\t\t// grammar is because both backtick and double quotes cannot be used\n\t\t// verbatim in a struct tag.\n\t\t//\n\t\t// Convert a single-quoted string to a double-quote string and rely on\n\t\t// strconv.Unquote to handle the rest.\n\t\tvar inEscape bool\n\t\tb := []byte{'\"'}\n\t\tn := len(`'`)\n\t\tfor len(in) > n {\n\t\t\tr, rn := utf8.DecodeRuneInString(in[n:])\n\t\t\tswitch {\n\t\t\tcase inEscape:\n\t\t\t\tif r == '\\'' {\n\t\t\t\t\tb = b[:len(b)-1] // remove escape character: `\\'` => `'`\n\t\t\t\t}\n\t\t\t\tinEscape = false\n\t\t\tcase r == '\\\\':\n\t\t\t\tinEscape = true\n\t\t\tcase r == '\"':\n\t\t\t\tb = append(b, '\\\\') // insert escape character: `\"` => `\\\"`\n\t\t\tcase r == '\\'':\n\t\t\t\tb = append(b, '\"')\n\t\t\t\tn += len(`'`)\n\t\t\t\tout, err := strconv.Unquote(string(b))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn in[:i], i, fmt.Errorf(\"invalid single-quoted string: %s\", in[:n])\n\t\t\t\t}\n\t\t\t\treturn out, n, nil\n\t\t\t}\n\t\t\tb = append(b, in[n:][:rn]...)\n\t\t\tn += rn\n\t\t}\n\t\tif n > 10 {\n\t\t\tn = 10 // limit the amount of context printed in the error\n\t\t}\n\t\t//lint:ignore ST1005 The ellipsis denotes truncated text\n\t\treturn in[:i], i, fmt.Errorf(\"single-quoted string not terminated: %s...\", in[:n])\n\tcase len(in) == 0:\n\t\treturn in[:i], i, io.ErrUnexpectedEOF\n\tdefault:\n\t\treturn in[:i], i, fmt.Errorf(\"invalid character %q at start of option (expecting Unicode letter or single quote)\", r)\n\t}\n}\n\nfunc isLetterOrDigit(r rune) bool {\n\treturn r == '_' || unicode.IsLetter(r) || unicode.IsNumber(r)\n}\n"
  },
  {
    "path": "staticcheck/sa5008/sa5008.go",
    "content": "package sa5008\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/staticcheck/fakereflect\"\n\t\"honnef.co/go/tools/staticcheck/fakexml\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA5008\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Invalid struct tag`,\n\t\tSince:    \"2019.2\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\timportsGoFlags := false\n\n\t// we use the AST instead of (*types.Package).Imports to work\n\t// around vendored packages in GOPATH mode. A vendored package's\n\t// path will include the vendoring subtree as a prefix.\n\tfor _, f := range pass.Files {\n\t\tfor _, imp := range f.Imports {\n\t\t\tv := imp.Path.Value\n\t\t\tif v[1:len(v)-1] == \"github.com/jessevdk/go-flags\" {\n\t\t\t\timportsGoFlags = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tfn := func(node ast.Node) {\n\t\tstructNode := node.(*ast.StructType)\n\t\tT := pass.TypesInfo.Types[structNode].Type.(*types.Struct)\n\t\trt := fakereflect.TypeAndCanAddr{\n\t\t\tType: T,\n\t\t}\n\t\tfor i, field := range structNode.Fields.List {\n\t\t\tif field.Tag == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttags, err := parseStructTag(field.Tag.Value[1 : len(field.Tag.Value)-1])\n\t\t\tif err != nil {\n\t\t\t\treport.Report(pass, field.Tag, fmt.Sprintf(\"unparseable struct tag: %s\", err))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor k, v := range tags {\n\t\t\t\tif len(v) > 1 {\n\t\t\t\t\tisGoFlagsTag := importsGoFlags &&\n\t\t\t\t\t\t(k == \"choice\" || k == \"optional-value\" || k == \"default\")\n\t\t\t\t\tif !isGoFlagsTag {\n\t\t\t\t\t\treport.Report(pass, field.Tag, fmt.Sprintf(\"duplicate struct tag %q\", k))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tswitch k {\n\t\t\t\tcase \"json\":\n\t\t\t\t\tcheckJSONTag(pass, field, v[0])\n\t\t\t\tcase \"xml\":\n\t\t\t\t\tif _, err := fakexml.StructFieldInfo(rt.Field(i)); err != nil {\n\t\t\t\t\t\treport.Report(pass, field.Tag, fmt.Sprintf(\"invalid XML tag: %s\", err))\n\t\t\t\t\t}\n\t\t\t\t\tcheckXMLTag(pass, field, v[0])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.StructType)(nil))\n\treturn nil, nil\n}\n\nfunc checkJSONTag(pass *analysis.Pass, field *ast.Field, tag string) {\n\tif pass.Pkg.Path() == \"encoding/json\" ||\n\t\tpass.Pkg.Path() == \"encoding/json_test\" ||\n\t\tpass.Pkg.Path() == \"encoding/json/v2\" ||\n\t\tpass.Pkg.Path() == \"encoding/json/v2_test\" {\n\t\t// don't flag malformed JSON tags in the encoding/json\n\t\t// package; it knows what it is doing, and it is testing\n\t\t// itself.\n\t\treturn\n\t}\n\t//lint:ignore SA9003 TODO(dh): should we flag empty tags?\n\tif len(tag) == 0 {\n\t}\n\n\tvalidateJSONTag(pass, field, tag)\n}\n\nfunc checkXMLTag(pass *analysis.Pass, field *ast.Field, tag string) {\n\t//lint:ignore SA9003 TODO(dh): should we flag empty tags?\n\tif len(tag) == 0 {\n\t}\n\tfields := strings.Split(tag, \",\")\n\tcounts := map[string]int{}\n\tfor _, s := range fields[1:] {\n\t\tswitch s {\n\t\tcase \"attr\", \"chardata\", \"cdata\", \"innerxml\", \"comment\":\n\t\t\tcounts[s]++\n\t\tcase \"omitempty\", \"any\":\n\t\t\tcounts[s]++\n\t\tcase \"\":\n\t\tdefault:\n\t\t\treport.Report(pass, field.Tag, fmt.Sprintf(\"invalid XML tag: unknown option %q\", s))\n\t\t}\n\t}\n\tfor k, v := range counts {\n\t\tif v > 1 {\n\t\t\treport.Report(pass, field.Tag, fmt.Sprintf(\"invalid XML tag: duplicate option %q\", k))\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5008/sa5008_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5008\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5008/structtag.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Copyright 2019 Dominik Honnef. All rights reserved.\n\npackage sa5008\n\nimport \"strconv\"\n\nfunc parseStructTag(tag string) (map[string][]string, error) {\n\t// FIXME(dh): detect missing closing quote\n\tout := map[string][]string{}\n\n\tfor tag != \"\" {\n\t\t// Skip leading space.\n\t\ti := 0\n\t\tfor i < len(tag) && tag[i] == ' ' {\n\t\t\ti++\n\t\t}\n\t\ttag = tag[i:]\n\t\tif tag == \"\" {\n\t\t\tbreak\n\t\t}\n\n\t\t// Scan to colon. A space, a quote or a control character is a syntax error.\n\t\t// Strictly speaking, control chars include the range [0x7f, 0x9f], not just\n\t\t// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters\n\t\t// as it is simpler to inspect the tag's bytes than the tag's runes.\n\t\ti = 0\n\t\tfor i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '\"' && tag[i] != 0x7f {\n\t\t\ti++\n\t\t}\n\t\tif i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '\"' {\n\t\t\tbreak\n\t\t}\n\t\tname := string(tag[:i])\n\t\ttag = tag[i+1:]\n\n\t\t// Scan quoted string to find value.\n\t\ti = 1\n\t\tfor i < len(tag) && tag[i] != '\"' {\n\t\t\tif tag[i] == '\\\\' {\n\t\t\t\ti++\n\t\t\t}\n\t\t\ti++\n\t\t}\n\t\tif i >= len(tag) {\n\t\t\tbreak\n\t\t}\n\t\tqvalue := string(tag[:i+1])\n\t\ttag = tag[i+1:]\n\n\t\tvalue, err := strconv.Unquote(qvalue)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tout[name] = append(out[name], value)\n\t}\n\treturn out, nil\n}\n"
  },
  {
    "path": "staticcheck/sa5008/testdata/go1.0/CheckStructTags/CheckStructTags.go",
    "content": "package pkg\n\nimport (\n\t\"encoding/xml\"\n\n\t_ \"github.com/jessevdk/go-flags\"\n)\n\ntype T1 struct {\n\tB  int                    `foo:\"\" foo:\"\"` //@ diag(`duplicate struct tag`)\n\tC  int                    `foo:\"\" bar:\"\"`\n\tD  int                    `json:\"-\"`\n\tE  int                    `json:\"\\\\\"`                   //@ diag(\"malformed `json` tag\")\n\tF  int                    `json:\",omitempty,omitempty\"` //@ diag(\"duplicate appearance of `omitempty` tag option\")\n\tG  int                    `json:\",omitempty,string\"`\n\tH  int                    `json:\",string,omitempty,string\"` //@ diag(\"duplicate appearance of `string` tag option\")\n\tI  int                    `json:\",foreign\"`                 //@ diag(\"invalid appearance of unknown `foreign` tag option\")\n\tJ  int                    `json:\",string\"`\n\tK  *int                   `json:\",string\"`\n\tL  **int                  `json:\",string\"` //@ diag(\"invalid appearance of `string` tag option\")\n\tM  complex128             `json:\",string\"` //@ diag(\"invalid appearance of `string` tag option\")\n\tN  int                    `json:\"some-name\"`\n\tO  int                    `json:\"some-name,omitzero,omitempty,format:'something,with,commas'\"`\n\tP  string                 `json:\"-,omitempty\"` //@ diag(`should encoding/json ignore this field or name it`)\n\tQ  string                 `json:\"'-',omitempty\"`\n\tR  map[string]interface{} `json:\"unknown\"`\n\tS  map[string]interface{} `json:\"inline\"`\n\tT2 `json:\",omitzero\"`\n\tT3 `json:\"bar,omitzero\"`\n}\n\ntype T2 struct {\n\tA int `xml:\",attr\"`\n\tB int `xml:\",chardata\"`\n\tC int `xml:\",cdata\"`\n\tD int `xml:\",innerxml\"`\n\tE int `xml:\",comment\"`\n\tF int `xml:\",omitempty\"`\n\tG int `xml:\",any\"`\n\tH int `xml:\",unknown\"` //@ diag(`unknown option`)\n\tI int `xml:\",any,any\"` //@ diag(`duplicate option`)\n\tJ int `xml:\"a>b>c,\"`\n}\n\ntype T3 struct {\n\tA int `json:\",omitempty\" xml:\",attr\"`\n\tB int `json:\",foreign\" xml:\",attr\"` //@ diag(\"invalid appearance of unknown `foreign` tag option\")\n}\n\ntype T4 struct {\n\tA int   `choice:\"foo\" choice:\"bar\"`\n\tB []int `optional-value:\"foo\" optional-value:\"bar\"`\n\tC []int `default:\"foo\" default:\"bar\"`\n\tD int   `json:\"foo\" json:\"bar\"` //@ diag(`duplicate struct tag`)\n}\n\nfunc xmlTags() {\n\ttype T1 struct {\n\t\tA       int      `xml:\",attr,innerxml\"` //@ diag(`invalid combination of options: \",attr,innerxml\"`)\n\t\tXMLName xml.Name `xml:\"ns \"`            //@ diag(`namespace without name: \"ns \"`)\n\t\tB       int      `xml:\"a>\"`             //@ diag(`trailing '>'`)\n\t\tC       int      `xml:\"a>b,attr\"`       //@ diag(`a>b chain not valid with attr flag`)\n\t}\n\ttype T6 struct {\n\t\tXMLName xml.Name `xml:\"foo\"`\n\t}\n\ttype T5 struct {\n\t\tF T6 `xml:\"f\"` //@ diag(`name \"f\" conflicts with name \"foo\" in example.com/CheckStructTags.T6.XMLName`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5008/testdata/go1.0/CheckStructTags2/CheckStructTags2.go",
    "content": "package pkg\n\ntype T5 struct {\n\tA int   `choice:\"foo\" choice:\"bar\"`                 //@ diag(`duplicate struct tag`)\n\tB []int `optional-value:\"foo\" optional-value:\"bar\"` //@ diag(`duplicate struct tag`)\n\tC []int `default:\"foo\" default:\"bar\"`               //@ diag(`duplicate struct tag`)\n}\n"
  },
  {
    "path": "staticcheck/sa5008/testdata/go1.0/vendor/github.com/jessevdk/go-flags/pkg.go",
    "content": "package pkg\n"
  },
  {
    "path": "staticcheck/sa5008/testdata/go1.18/CheckStructTags/generics.go",
    "content": "package pkg\n\ntype S1[T any] struct {\n\t// flag, 'any' is too permissive\n\tF T `json:\",string\"` //@ diag(\"invalid appearance of `string` tag option\")\n}\n\ntype S2[T int | string] struct {\n\t// don't flag, all types in T are okay\n\tF T `json:\",string\"`\n}\n\ntype S3[T int | complex128] struct {\n\t// flag, can't use ,string on complex128\n\tF T `json:\",string\"` //@ diag(\"invalid appearance of `string` tag option\")\n}\n\ntype S4[T int | string] struct {\n\t// don't flag, pointers to stringable types are also stringable\n\tF *T `json:\",string\"`\n}\n\ntype S5[T ~int | ~string, PT ~int | ~string | ~*T] struct {\n\t// don't flag, pointers to stringable types are also stringable\n\tF PT `json:\",string\"`\n}\n\ntype S6[T int | complex128] struct {\n\t// flag, pointers to non-stringable types aren't stringable, either\n\tF *T `json:\",string\"` //@ diag(\"invalid appearance of `string` tag option\")\n}\n\ntype S7[T int | complex128, PT *T] struct {\n\t// flag, pointers to non-stringable types aren't stringable, either\n\tF PT `json:\",string\"` //@ diag(\"invalid appearance of `string` tag option\")\n}\n\ntype S8[T int, PT *T | complex128] struct {\n\t// do flag, variation of S7\n\tF PT `json:\",string\"` //@ diag(\"invalid appearance of `string` tag option\")\n}\n\ntype S9[T int | *bool, PT *T | float64, PPT *PT | string] struct {\n\t// do flag, multiple levels of pointers aren't allowed\n\tF PPT `json:\",string\"` //@ diag(\"invalid appearance of `string` tag option\")\n}\n\ntype S10[T1 *T2, T2 *T1] struct {\n\t// do flag, don't get stuck in an infinite loop\n\tF T1 `json:\",string\"` //@ diag(\"invalid appearance of `string` tag option\")\n}\n\ntype S11[E ~int | ~complex128, T ~*E] struct {\n\tF T `json:\",string\"` //@ diag(\"invalid appearance of `string` tag option\")\n}\n"
  },
  {
    "path": "staticcheck/sa5009/sa5009.go",
    "content": "package sa5009\n\nimport (\n\t\"fmt\"\n\t\"go/constant\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\t\"honnef.co/go/tools/printf\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA5009\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Invalid Printf call`,\n\t\tSince:    \"2019.2\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\n// TODO(dh): detect printf wrappers\nvar rules = map[string]callcheck.Check{\n\t\"fmt.Errorf\":                  func(call *callcheck.Call) { check(call, 0, 1) },\n\t\"fmt.Printf\":                  func(call *callcheck.Call) { check(call, 0, 1) },\n\t\"fmt.Sprintf\":                 func(call *callcheck.Call) { check(call, 0, 1) },\n\t\"fmt.Fprintf\":                 func(call *callcheck.Call) { check(call, 1, 2) },\n\t\"golang.org/x/xerrors.Errorf\": func(call *callcheck.Call) { check(call, 0, 1) },\n}\n\ntype verbFlag int\n\nconst (\n\tisInt verbFlag = 1 << iota\n\tisBool\n\tisFP\n\tisString\n\tisPointer\n\t// Verbs that accept \"pseudo pointers\" will sometimes dereference\n\t// non-nil pointers. For example, %x on a non-nil *struct will print the\n\t// individual fields, but on a nil pointer it will print the address.\n\tisPseudoPointer\n\tisSlice\n\tisAny\n\tnoRecurse\n)\n\nvar verbs = [...]verbFlag{\n\t'b': isPseudoPointer | isInt | isFP,\n\t'c': isInt,\n\t'd': isPseudoPointer | isInt,\n\t'e': isFP,\n\t'E': isFP,\n\t'f': isFP,\n\t'F': isFP,\n\t'g': isFP,\n\t'G': isFP,\n\t'o': isPseudoPointer | isInt,\n\t'O': isPseudoPointer | isInt,\n\t'p': isSlice | isPointer | noRecurse,\n\t'q': isInt | isString,\n\t's': isString,\n\t't': isBool,\n\t'T': isAny,\n\t'U': isInt,\n\t'v': isAny,\n\t'X': isPseudoPointer | isInt | isFP | isString,\n\t'x': isPseudoPointer | isInt | isFP | isString,\n}\n\nfunc check(call *callcheck.Call, fIdx, vIdx int) {\n\tf := call.Args[fIdx]\n\tvar args []ir.Value\n\tswitch v := call.Args[vIdx].Value.Value.(type) {\n\tcase *ir.Slice:\n\t\tvar ok bool\n\t\targs, ok = irutil.Vararg(v)\n\t\tif !ok {\n\t\t\t// We don't know what the actual arguments to the function are\n\t\t\treturn\n\t\t}\n\tcase *ir.Const:\n\t\t// nil, i.e. no arguments\n\tdefault:\n\t\t// We don't know what the actual arguments to the function are\n\t\treturn\n\t}\n\tcheckImpl(f, f.Value.Value, args)\n}\n\nfunc checkImpl(carg *callcheck.Argument, f ir.Value, args []ir.Value) {\n\tvar msCache *typeutil.MethodSetCache\n\tif f.Parent() != nil {\n\t\tmsCache = &f.Parent().Prog.MethodSets\n\t}\n\n\telem := func(T types.Type, verb rune) ([]types.Type, bool) {\n\t\tif verbs[verb]&noRecurse != 0 {\n\t\t\treturn []types.Type{T}, false\n\t\t}\n\t\tswitch T := T.(type) {\n\t\tcase *types.Slice:\n\t\t\tif verbs[verb]&isSlice != 0 {\n\t\t\t\treturn []types.Type{T}, false\n\t\t\t}\n\t\t\tif verbs[verb]&isString != 0 && types.Identical(T.Elem().Underlying(), types.Typ[types.Byte]) {\n\t\t\t\treturn []types.Type{T}, false\n\t\t\t}\n\t\t\treturn []types.Type{T.Elem()}, true\n\t\tcase *types.Map:\n\t\t\tkey := T.Key()\n\t\t\tval := T.Elem()\n\t\t\treturn []types.Type{key, val}, true\n\t\tcase *types.Struct:\n\t\t\tout := make([]types.Type, 0, T.NumFields())\n\t\t\tfor field := range T.Fields() {\n\t\t\t\tout = append(out, field.Type())\n\t\t\t}\n\t\t\treturn out, true\n\t\tcase *types.Array:\n\t\t\treturn []types.Type{T.Elem()}, true\n\t\tdefault:\n\t\t\treturn []types.Type{T}, false\n\t\t}\n\t}\n\tisInfo := func(T types.Type, info types.BasicInfo) bool {\n\t\tbasic, ok := T.Underlying().(*types.Basic)\n\t\treturn ok && basic.Info()&info != 0\n\t}\n\n\tisFormatter := func(T types.Type, ms *types.MethodSet) bool {\n\t\tsel := ms.Lookup(nil, \"Format\")\n\t\tif sel == nil {\n\t\t\treturn false\n\t\t}\n\t\tfn, ok := sel.Obj().(*types.Func)\n\t\tif !ok {\n\t\t\t// should be unreachable\n\t\t\treturn false\n\t\t}\n\t\tsig := fn.Type().(*types.Signature)\n\t\tif sig.Params().Len() != 2 {\n\t\t\treturn false\n\t\t}\n\t\t// TODO(dh): check the types of the arguments for more\n\t\t// precision\n\t\tif sig.Results().Len() != 0 {\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t}\n\n\tvar seen typeutil.Map[struct{}]\n\tvar checkType func(verb rune, T types.Type, top bool) bool\n\tcheckType = func(verb rune, T types.Type, top bool) bool {\n\t\tif top {\n\t\t\tseen = typeutil.Map[struct{}]{}\n\t\t}\n\t\tif _, ok := seen.At(T); ok {\n\t\t\treturn true\n\t\t}\n\t\tseen.Set(T, struct{}{})\n\t\tif int(verb) >= len(verbs) {\n\t\t\t// Unknown verb\n\t\t\treturn true\n\t\t}\n\n\t\tflags := verbs[verb]\n\t\tif flags == 0 {\n\t\t\t// Unknown verb\n\t\t\treturn true\n\t\t}\n\n\t\tms := msCache.MethodSet(T)\n\t\tif isFormatter(T, ms) {\n\t\t\t// the value is responsible for formatting itself\n\t\t\treturn true\n\t\t}\n\n\t\tif flags&isString != 0 && (types.Implements(T, knowledge.Interfaces[\"fmt.Stringer\"]) || types.Implements(T, knowledge.Interfaces[\"error\"])) {\n\t\t\t// Check for stringer early because we're about to dereference\n\t\t\treturn true\n\t\t}\n\n\t\tT = T.Underlying()\n\t\tif flags&(isPointer|isPseudoPointer) == 0 && top {\n\t\t\tT = typeutil.Dereference(T)\n\t\t}\n\t\tif flags&isPseudoPointer != 0 && top {\n\t\t\tt := typeutil.Dereference(T)\n\t\t\tif _, ok := t.Underlying().(*types.Struct); ok {\n\t\t\t\tT = t\n\t\t\t}\n\t\t}\n\n\t\tif _, ok := T.(*types.Interface); ok {\n\t\t\t// We don't know what's in the interface\n\t\t\treturn true\n\t\t}\n\n\t\tvar info types.BasicInfo\n\t\tif flags&isInt != 0 {\n\t\t\tinfo |= types.IsInteger\n\t\t}\n\t\tif flags&isBool != 0 {\n\t\t\tinfo |= types.IsBoolean\n\t\t}\n\t\tif flags&isFP != 0 {\n\t\t\tinfo |= types.IsFloat | types.IsComplex\n\t\t}\n\t\tif flags&isString != 0 {\n\t\t\tinfo |= types.IsString\n\t\t}\n\n\t\tif info != 0 && isInfo(T, info) {\n\t\t\treturn true\n\t\t}\n\n\t\tif flags&isString != 0 {\n\t\t\tisStringyElem := func(typ types.Type) bool {\n\t\t\t\tif typ, ok := typ.Underlying().(*types.Basic); ok {\n\t\t\t\t\treturn typ.Kind() == types.Byte\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tswitch T := T.(type) {\n\t\t\tcase *types.Slice:\n\t\t\t\tif isStringyElem(T.Elem()) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\tcase *types.Array:\n\t\t\t\tif isStringyElem(T.Elem()) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t\tif types.Implements(T, knowledge.Interfaces[\"fmt.Stringer\"]) || types.Implements(T, knowledge.Interfaces[\"error\"]) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\tif flags&isPointer != 0 && typeutil.IsPointerLike(T) {\n\t\t\treturn true\n\t\t}\n\t\tif flags&isPseudoPointer != 0 {\n\t\t\tswitch U := T.Underlying().(type) {\n\t\t\tcase *types.Pointer:\n\t\t\t\tif !top {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\n\t\t\t\tif _, ok := U.Elem().Underlying().(*types.Struct); !ok {\n\t\t\t\t\t// TODO(dh): can this condition ever be false? For\n\t\t\t\t\t// *T, if T is a struct, we'll already have\n\t\t\t\t\t// dereferenced it, meaning the *types.Pointer\n\t\t\t\t\t// branch couldn't have been taken. For T that\n\t\t\t\t\t// aren't structs, this condition will always\n\t\t\t\t\t// evaluate to true.\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\tcase *types.Chan, *types.Signature:\n\t\t\t\t// Channels and functions are always treated as\n\t\t\t\t// pointers and never recursed into.\n\t\t\t\treturn true\n\t\t\tcase *types.Basic:\n\t\t\t\tif U.Kind() == types.UnsafePointer {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\tcase *types.Interface:\n\t\t\t\t// we will already have bailed if the type is an\n\t\t\t\t// interface.\n\t\t\t\tpanic(\"unreachable\")\n\t\t\tdefault:\n\t\t\t\t// other pointer-like types, such as maps or slices,\n\t\t\t\t// will be printed element-wise.\n\t\t\t}\n\t\t}\n\n\t\tif flags&isSlice != 0 {\n\t\t\tif _, ok := T.(*types.Slice); ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\tif flags&isAny != 0 {\n\t\t\treturn true\n\t\t}\n\n\t\telems, ok := elem(T.Underlying(), verb)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\t\tfor _, elem := range elems {\n\t\t\tif !checkType(verb, elem, false) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\treturn true\n\t}\n\n\tk, ok := irutil.Flatten(f).(*ir.Const)\n\tif !ok {\n\t\treturn\n\t}\n\tactions, err := printf.Parse(constant.StringVal(k.Value))\n\tif err != nil {\n\t\tcarg.Invalid(\"couldn't parse format string\")\n\t\treturn\n\t}\n\n\tptr := 1\n\thasExplicit := false\n\n\tcheckStar := func(verb printf.Verb, star printf.Argument) bool {\n\t\tif star, ok := star.(printf.Star); ok {\n\t\t\tidx := 0\n\t\t\tif star.Index == -1 {\n\t\t\t\tidx = ptr\n\t\t\t\tptr++\n\t\t\t} else {\n\t\t\t\thasExplicit = true\n\t\t\t\tidx = star.Index\n\t\t\t\tptr = star.Index + 1\n\t\t\t}\n\t\t\tif idx == 0 {\n\t\t\t\tcarg.Invalid(fmt.Sprintf(\"Printf format %s reads invalid arg 0; indices are 1-based\", verb.Raw))\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif idx > len(args) {\n\t\t\t\tcarg.Invalid(\n\t\t\t\t\tfmt.Sprintf(\"Printf format %s reads arg #%d, but call has only %d args\",\n\t\t\t\t\t\tverb.Raw, idx, len(args)))\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif arg, ok := args[idx-1].(*ir.MakeInterface); ok {\n\t\t\t\tif !isInfo(arg.X.Type(), types.IsInteger) {\n\t\t\t\t\tcarg.Invalid(fmt.Sprintf(\"Printf format %s reads non-int arg #%d as argument of *\", verb.Raw, idx))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\n\t// We only report one problem per format string. Making a\n\t// mistake with an index tends to invalidate all future\n\t// implicit indices.\n\tfor _, action := range actions {\n\t\tverb, ok := action.(printf.Verb)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif !checkStar(verb, verb.Width) || !checkStar(verb, verb.Precision) {\n\t\t\treturn\n\t\t}\n\n\t\toff := ptr\n\t\tif verb.Value != -1 {\n\t\t\thasExplicit = true\n\t\t\toff = verb.Value\n\t\t}\n\t\tif off > len(args) {\n\t\t\tcarg.Invalid(\n\t\t\t\tfmt.Sprintf(\"Printf format %s reads arg #%d, but call has only %d args\",\n\t\t\t\t\tverb.Raw, off, len(args)))\n\t\t\treturn\n\t\t} else if verb.Value == 0 && verb.Letter != '%' {\n\t\t\tcarg.Invalid(fmt.Sprintf(\"Printf format %s reads invalid arg 0; indices are 1-based\", verb.Raw))\n\t\t\treturn\n\t\t} else if off != 0 {\n\t\t\targ, ok := args[off-1].(*ir.MakeInterface)\n\t\t\tif ok {\n\t\t\t\tif !checkType(verb.Letter, arg.X.Type(), true) {\n\t\t\t\t\tcarg.Invalid(fmt.Sprintf(\"Printf format %s has arg #%d of wrong type %s\",\n\t\t\t\t\t\tverb.Raw, ptr, args[ptr-1].(*ir.MakeInterface).X.Type()))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch verb.Value {\n\t\tcase -1:\n\t\t\t// Consume next argument\n\t\t\tptr++\n\t\tcase 0:\n\t\t\t// Don't consume any arguments\n\t\tdefault:\n\t\t\tptr = verb.Value + 1\n\t\t}\n\t}\n\n\tif !hasExplicit && ptr <= len(args) {\n\t\tcarg.Invalid(fmt.Sprintf(\"Printf call needs %d args but has %d args\", ptr-1, len(args)))\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5009/sa5009_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5009\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5009/testdata/go1.0/CheckPrintf/CheckPrintf.go",
    "content": "// Package pkg is amazing.\npackage pkg\n\nimport (\n\t\"fmt\"\n\t\"math/big\"\n\t\"os\"\n\t\"unsafe\"\n)\n\ntype Error int\n\nfunc (Error) Error() string { return \"\" }\n\nfunc fn() {\n\tvar b bool\n\tvar i int\n\tvar r rune\n\tvar s string\n\tvar x float64\n\tvar p *int\n\tvar imap map[int]int\n\tvar fslice []float64\n\tvar c complex64\n\t// Some good format/argtypes\n\tfmt.Printf(\"\")\n\tfmt.Printf(\"%b %b %b\", 3, i, x)\n\tfmt.Printf(\"%c %c %c %c\", 3, i, 'x', r)\n\tfmt.Printf(\"%d %d %d\", 3, i, imap)\n\tfmt.Printf(\"%e %e %e %e\", 3e9, x, fslice, c)\n\tfmt.Printf(\"%E %E %E %E\", 3e9, x, fslice, c)\n\tfmt.Printf(\"%f %f %f %f\", 3e9, x, fslice, c)\n\tfmt.Printf(\"%F %F %F %F\", 3e9, x, fslice, c)\n\tfmt.Printf(\"%g %g %g %g\", 3e9, x, fslice, c)\n\tfmt.Printf(\"%G %G %G %G\", 3e9, x, fslice, c)\n\tfmt.Printf(\"%b %b %b %b\", 3e9, x, fslice, c)\n\tfmt.Printf(\"%o %o\", 3, i)\n\tfmt.Printf(\"%p\", p)\n\tfmt.Printf(\"%q %q %q %q\", 3, i, 'x', r)\n\tfmt.Printf(\"%s %s %s\", \"hi\", s, []byte{65})\n\tfmt.Printf(\"%t %t\", true, b)\n\tfmt.Printf(\"%T %T\", 3, i)\n\tfmt.Printf(\"%U %U\", 3, i)\n\tfmt.Printf(\"%v %v\", 3, i)\n\tfmt.Printf(\"%x %x %x %x\", 3, i, \"hi\", s)\n\tfmt.Printf(\"%X %X %X %X\", 3, i, \"hi\", s)\n\tfmt.Printf(\"%.*s %d %g\", 3, \"hi\", 23, 2.3)\n\tfmt.Printf(\"%s\", &stringerv)\n\tfmt.Printf(\"%v\", &stringerv)\n\tfmt.Printf(\"%T\", &stringerv)\n\tfmt.Printf(\"%s\", &embeddedStringerv)\n\tfmt.Printf(\"%v\", &embeddedStringerv)\n\tfmt.Printf(\"%T\", &embeddedStringerv)\n\tfmt.Printf(\"%v\", notstringerv)\n\tfmt.Printf(\"%T\", notstringerv)\n\tfmt.Printf(\"%q\", stringerarrayv)\n\tfmt.Printf(\"%v\", stringerarrayv)\n\tfmt.Printf(\"%s\", stringerarrayv)\n\tfmt.Printf(\"%v\", notstringerarrayv)\n\tfmt.Printf(\"%T\", notstringerarrayv)\n\tfmt.Printf(\"%d\", new(fmt.Formatter))\n\tfmt.Printf(\"%f\", new(big.Float))\n\tfmt.Printf(\"%*%\", 2)               // Ridiculous but allowed.\n\tfmt.Printf(\"%s\", interface{}(nil)) // Nothing useful we can say.\n\n\tfmt.Printf(\"%g\", 1+2i)\n\tfmt.Printf(\"%#e %#E %#f %#F %#g %#G\", 1.2, 1.2, 1.2, 1.2, 1.2, 1.2) // OK since Go 1.9\n\t// Some bad format/argTypes\n\tfmt.Printf(\"%b\", \"hi\")             //@ diag(`Printf format %b has arg #1 of wrong type string`)\n\t_ = fmt.Sprintf(\"%b\", \"hi\")        //@ diag(`Printf format %b has arg #1 of wrong type string`)\n\tfmt.Fprintf(os.Stdout, \"%b\", \"hi\") //@ diag(`Printf format %b has arg #1 of wrong type string`)\n\tfmt.Printf(\"%t\", c)                //@ diag(`Printf format %t has arg #1 of wrong type complex64`)\n\tfmt.Printf(\"%t\", 1+2i)             //@ diag(`Printf format %t has arg #1 of wrong type complex128`)\n\tfmt.Printf(\"%c\", 2.3)              //@ diag(`Printf format %c has arg #1 of wrong type float64`)\n\tfmt.Printf(\"%d\", 2.3)              //@ diag(`Printf format %d has arg #1 of wrong type float64`)\n\tfmt.Printf(\"%e\", \"hi\")             //@ diag(`Printf format %e has arg #1 of wrong type string`)\n\tfmt.Printf(\"%E\", true)             //@ diag(`Printf format %E has arg #1 of wrong type bool`)\n\tfmt.Printf(\"%f\", \"hi\")             //@ diag(`Printf format %f has arg #1 of wrong type string`)\n\tfmt.Printf(\"%F\", 'x')              //@ diag(`Printf format %F has arg #1 of wrong type rune`)\n\tfmt.Printf(\"%g\", \"hi\")             //@ diag(`Printf format %g has arg #1 of wrong type string`)\n\tfmt.Printf(\"%g\", imap)             //@ diag(`Printf format %g has arg #1 of wrong type map[int]int`)\n\tfmt.Printf(\"%G\", i)                //@ diag(`Printf format %G has arg #1 of wrong type int`)\n\tfmt.Printf(\"%o\", x)                //@ diag(`Printf format %o has arg #1 of wrong type float64`)\n\tfmt.Printf(\"%p\", 23)               //@ diag(`Printf format %p has arg #1 of wrong type int`)\n\tfmt.Printf(\"%q\", x)                //@ diag(`Printf format %q has arg #1 of wrong type float64`)\n\tfmt.Printf(\"%s\", b)                //@ diag(`Printf format %s has arg #1 of wrong type bool`)\n\tfmt.Printf(\"%s\", byte(65))         //@ diag(`Printf format %s has arg #1 of wrong type byte`)\n\tfmt.Printf(\"%t\", 23)               //@ diag(`Printf format %t has arg #1 of wrong type int`)\n\tfmt.Printf(\"%U\", x)                //@ diag(`Printf format %U has arg #1 of wrong type float64`)\n\tfmt.Printf(\"%X\", 2.3)\n\tfmt.Printf(\"%X\", 2+3i)\n\tfmt.Printf(\"%s\", stringerv)                 //@ diag(`Printf format %s has arg #1 of wrong type example.com/CheckPrintf.ptrStringer`)\n\tfmt.Printf(\"%t\", stringerv)                 //@ diag(`Printf format %t has arg #1 of wrong type example.com/CheckPrintf.ptrStringer`)\n\tfmt.Printf(\"%s\", embeddedStringerv)         //@ diag(`Printf format %s has arg #1 of wrong type example.com/CheckPrintf.embeddedStringer`)\n\tfmt.Printf(\"%t\", embeddedStringerv)         //@ diag(`Printf format %t has arg #1 of wrong type example.com/CheckPrintf.embeddedStringer`)\n\tfmt.Printf(\"%q\", notstringerv)              //@ diag(`Printf format %q has arg #1 of wrong type example.com/CheckPrintf.notstringer`)\n\tfmt.Printf(\"%t\", notstringerv)              //@ diag(`Printf format %t has arg #1 of wrong type example.com/CheckPrintf.notstringer`)\n\tfmt.Printf(\"%t\", stringerarrayv)            //@ diag(`Printf format %t has arg #1 of wrong type example.com/CheckPrintf.stringerarray`)\n\tfmt.Printf(\"%t\", notstringerarrayv)         //@ diag(`Printf format %t has arg #1 of wrong type example.com/CheckPrintf.notstringerarray`)\n\tfmt.Printf(\"%q\", notstringerarrayv)         //@ diag(`Printf format %q has arg #1 of wrong type example.com/CheckPrintf.notstringerarray`)\n\tfmt.Printf(\"%d\", BoolFormatter(true))       //@ diag(`Printf format %d has arg #1 of wrong type example.com/CheckPrintf.BoolFormatter`)\n\tfmt.Printf(\"%z\", FormatterVal(true))        // correct (the type is responsible for formatting)\n\tfmt.Printf(\"%d\", FormatterVal(true))        // correct (the type is responsible for formatting)\n\tfmt.Printf(\"%s\", nonemptyinterface)         // correct (the type is responsible for formatting)\n\tfmt.Printf(\"%.*s %d %6g\", 3, \"hi\", 23, 'x') //@ diag(`Printf format %6g has arg #4 of wrong type rune`)\n\tfmt.Printf(\"%s\", \"hi\", 3)                   //@ diag(`Printf call needs 1 args but has 2 args`)\n\tfmt.Printf(\"%\"+(\"s\"), \"hi\", 3)              //@ diag(`Printf call needs 1 args but has 2 args`)\n\tfmt.Printf(\"%s%%%d\", \"hi\", 3)               // correct\n\tfmt.Printf(\"%08s\", \"woo\")                   // correct\n\tfmt.Printf(\"% 8s\", \"woo\")                   // correct\n\tfmt.Printf(\"%.*d\", 3, 3)                    // correct\n\tfmt.Printf(\"%.*d x\", 3, 3, 3, 3)            //@ diag(`Printf call needs 2 args but has 4 args`)\n\tfmt.Printf(\"%.*d x\", \"hi\", 3)               //@ diag(`Printf format %.*d reads non-int arg #1 as argument of *`)\n\tfmt.Printf(\"%.*d x\", i, 3)                  // correct\n\tfmt.Printf(\"%.*d x\", s, 3)                  //@ diag(`Printf format %.*d reads non-int arg #1 as argument of *`)\n\tfmt.Printf(\"%*% x\", 0.22)                   //@ diag(`Printf format %*% reads non-int arg #1 as argument of *`)\n\tfmt.Printf(\"%q %q\", multi()...)             // ok\n\tfmt.Printf(\"%#q\", `blah`)                   // ok\n\tconst format = \"%s %s\\n\"\n\tfmt.Printf(format, \"hi\", \"there\")\n\tfmt.Printf(format, \"hi\")              //@ diag(`Printf format %s reads arg #2, but call has only 1 args`)\n\tfmt.Printf(\"%s %d %.3v %q\", \"str\", 4) //@ diag(`Printf format %.3v reads arg #3, but call has only 2 args`)\n\n\tfmt.Printf(\"%#s\", FormatterVal(true)) // correct (the type is responsible for formatting)\n\tfmt.Printf(\"d%\", 2)                   //@ diag(`couldn't parse format string`)\n\tfmt.Printf(\"%d\", percentDV)\n\tfmt.Printf(\"%d\", &percentDV)\n\tfmt.Printf(\"%d\", notPercentDV)  //@ diag(`Printf format %d has arg #1 of wrong type example.com/CheckPrintf.notPercentDStruct`)\n\tfmt.Printf(\"%d\", &notPercentDV) //@ diag(`Printf format %d has arg #1 of wrong type *example.com/CheckPrintf.notPercentDStruct`)\n\tfmt.Printf(\"%p\", &notPercentDV) // Works regardless: we print it as a pointer.\n\tfmt.Printf(\"%q\", &percentDV)    //@ diag(`Printf format %q has arg #1 of wrong type *example.com/CheckPrintf.percentDStruct`)\n\tfmt.Printf(\"%s\", percentSV)\n\tfmt.Printf(\"%s\", &percentSV)\n\t// Good argument reorderings.\n\tfmt.Printf(\"%[1]d\", 3)\n\tfmt.Printf(\"%[1]*d\", 3, 1)\n\tfmt.Printf(\"%[2]*[1]d\", 1, 3)\n\tfmt.Printf(\"%[2]*.[1]*[3]d\", 2, 3, 4)\n\tfmt.Fprintf(os.Stderr, \"%[2]*.[1]*[3]d\", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly.\n\t// Bad argument reorderings.\n\tfmt.Printf(\"%[xd\", 3)                      //@ diag(`couldn't parse format string`)\n\tfmt.Printf(\"%[x]d x\", 3)                   //@ diag(`couldn't parse format string`)\n\tfmt.Printf(\"%[3]*s x\", \"hi\", 2)            //@ diag(`Printf format %[3]*s reads arg #3, but call has only 2 args`)\n\tfmt.Printf(\"%[3]d x\", 2)                   //@ diag(`Printf format %[3]d reads arg #3, but call has only 1 args`)\n\tfmt.Printf(\"%[2]*.[1]*[3]d x\", 2, \"hi\", 4) //@ diag(`Printf format %[2]*.[1]*[3]d reads non-int arg #2 as argument of *`)\n\tfmt.Printf(\"%[0]s x\", \"arg1\")              //@ diag(`Printf format %[0]s reads invalid arg 0; indices are 1-based`)\n\tfmt.Printf(\"%[0]d x\", 1)                   //@ diag(`Printf format %[0]d reads invalid arg 0; indices are 1-based`)\n\n\t// Interfaces can be used with any verb.\n\tvar iface interface {\n\t\tToTheMadness() bool // Method ToTheMadness usually returns false\n\t}\n\tfmt.Printf(\"%f\", iface) // ok: fmt treats interfaces as transparent and iface may well have a float concrete type\n\t// Can print functions in many ways\n\tfmt.Printf(\"%s\", someFunction) //@ diag(`Printf format %s has arg #1 of wrong type func()`)\n\tfmt.Printf(\"%d\", someFunction) // ok: maybe someone wants to see the pointer\n\tfmt.Printf(\"%v\", someFunction) // ok: maybe someone wants to see the pointer in decimal\n\tfmt.Printf(\"%p\", someFunction) // ok: maybe someone wants to see the pointer\n\tfmt.Printf(\"%T\", someFunction) // ok: maybe someone wants to see the type\n\t// Bug: used to recur forever.\n\tfmt.Printf(\"%p %x\", recursiveStructV, recursiveStructV.next)\n\tfmt.Printf(\"%p %x\", recursiveStruct1V, recursiveStruct1V.next)\n\tfmt.Printf(\"%p %x\", recursiveSliceV, recursiveSliceV)\n\t//fmt.Printf(\"%p %x\", recursiveMapV, recursiveMapV)\n\n\t// indexed arguments\n\tfmt.Printf(\"%d %[3]d %d %[2]d x\", 1, 2, 3, 4)    // OK\n\tfmt.Printf(\"%d %[0]d %d %[2]d x\", 1, 2, 3, 4)    //@ diag(`Printf format %[0]d reads invalid arg 0; indices are 1-based`)\n\tfmt.Printf(\"%d %[3]d %d %[-2]d x\", 1, 2, 3, 4)   //@ diag(`couldn't parse format string`)\n\tfmt.Printf(\"%d %[3]d %d %[5]d x\", 1, 2, 3, 4)    //@ diag(`Printf format %[5]d reads arg #5, but call has only 4 args`)\n\tfmt.Printf(\"%d %[3]d %-10d %[2]d x\", 1, 2, 3)    //@ diag(`Printf format %-10d reads arg #4, but call has only 3 args`)\n\tfmt.Printf(\"%[1][3]d x\", 1, 2)                   //@ diag(`couldn't parse format string`)\n\tfmt.Printf(\"%[1]d x\", 1, 2)                      // OK\n\tfmt.Printf(\"%d %[3]d %d %[2]d x\", 1, 2, 3, 4, 5) // OK\n\n\tfmt.Printf(someString(), \"hello\") // OK\n\n\t// d accepts pointers as long as they're not to structs.\n\t// pointers to structs are dereferenced and walked.\n\tfmt.Printf(\"%d\", &s)\n\n\t// staticcheck's own checks, based on bugs in go vet; see https://github.com/golang/go/issues/27672\n\t{\n\n\t\ttype T2 struct {\n\t\t\tX string\n\t\t}\n\n\t\ttype T1 struct {\n\t\t\tX *T2\n\t\t}\n\t\tx1 := []string{\"hi\"}\n\t\tt1 := T1{&T2{\"hi\"}}\n\n\t\tfmt.Printf(\"%s\\n\", &x1)\n\t\tfmt.Printf(\"%s\\n\", t1) //@ diag(`Printf format %s has arg #1 of wrong type example.com/CheckPrintf.T1`)\n\t\tvar x2 struct{ A *int }\n\t\tfmt.Printf(\"%p\\n\", x2) //@ diag(`Printf format %p has arg #1 of wrong type struct{A *int}`)\n\t\tvar x3 [2]int\n\t\tfmt.Printf(\"%p\", x3) //@ diag(`Printf format %p has arg #1 of wrong type [2]int`)\n\n\t\tue := unexportedError{nil}\n\t\tfmt.Printf(\"%s\", ue)\n\t}\n\n\t// staticcheck's own checks, based on our own bugs\n\tfmt.Printf(\"%s\", Error(0))\n\tfmt.Printf(\"%x\", unsafe.Pointer(uintptr(0)))\n}\n\nfunc someString() string { return \"X\" }\n\n// A function we use as a function value; it has no other purpose.\nfunc someFunction() {}\n\n// multi is used by the test.\nfunc multi() []interface{} {\n\treturn nil\n}\n\ntype stringer int\n\nfunc (stringer) String() string { return \"string\" }\n\ntype ptrStringer float64\n\nvar stringerv ptrStringer\n\nfunc (*ptrStringer) String() string {\n\treturn \"string\"\n}\n\ntype embeddedStringer struct {\n\tfoo string\n\tptrStringer\n\tbar int\n}\n\nvar embeddedStringerv embeddedStringer\n\ntype notstringer struct {\n\tf float64\n}\n\nvar notstringerv notstringer\n\ntype stringerarray [4]float64\n\nfunc (stringerarray) String() string {\n\treturn \"string\"\n}\n\nvar stringerarrayv stringerarray\n\ntype notstringerarray [4]float64\n\nvar notstringerarrayv notstringerarray\n\nvar nonemptyinterface = interface {\n\tf()\n}(nil)\n\n// A data type we can print with \"%d\".\ntype percentDStruct struct {\n\ta int\n\tb []byte\n\tc *float64\n}\n\nvar percentDV percentDStruct\n\n// A data type we cannot print correctly with \"%d\".\ntype notPercentDStruct struct {\n\ta int\n\tb []byte\n\tc bool\n}\n\nvar notPercentDV notPercentDStruct\n\n// A data type we can print with \"%s\".\ntype percentSStruct struct {\n\ta string\n\tb []byte\n\tC stringerarray\n}\n\nvar percentSV percentSStruct\n\ntype BoolFormatter bool\n\nfunc (*BoolFormatter) Format(fmt.State, rune) {\n}\n\n// Formatter with value receiver\ntype FormatterVal bool\n\nfunc (FormatterVal) Format(fmt.State, rune) {\n}\n\ntype RecursiveSlice []RecursiveSlice\n\nvar recursiveSliceV = &RecursiveSlice{}\n\ntype RecursiveMap map[int]RecursiveMap\n\nvar recursiveMapV = make(RecursiveMap)\n\ntype RecursiveStruct struct {\n\tnext *RecursiveStruct\n}\n\nvar recursiveStructV = &RecursiveStruct{}\n\ntype RecursiveStruct1 struct {\n\tnext *RecursiveStruct2\n}\n\ntype RecursiveStruct2 struct {\n\tnext *RecursiveStruct1\n}\n\nvar recursiveStruct1V = &RecursiveStruct1{}\n\ntype unexportedInterface struct {\n\tf interface{}\n}\n\n// Issue 17798: unexported ptrStringer cannot be formatted.\ntype unexportedStringer struct {\n\tt ptrStringer\n}\ntype unexportedStringerOtherFields struct {\n\ts string\n\tt ptrStringer\n\tS string\n}\n\n// Issue 17798: unexported error cannot be formatted.\ntype unexportedError struct {\n\te error\n}\ntype unexportedErrorOtherFields struct {\n\ts string\n\te error\n\tS string\n}\n\ntype errorer struct{}\n\nfunc (e errorer) Error() string { return \"errorer\" }\n\ntype unexportedCustomError struct {\n\te errorer\n}\n\ntype errorInterface interface {\n\terror\n\tExtraMethod()\n}\n\ntype unexportedErrorInterface struct {\n\te errorInterface\n}\n\nfunc UnexportedStringerOrError() {\n\tfmt.Printf(\"%s\", unexportedInterface{\"foo\"}) // ok; prints {foo}\n\tfmt.Printf(\"%s\", unexportedInterface{3})     // ok; we can't see the problem\n\n\tus := unexportedStringer{}\n\tfmt.Printf(\"%s\", us)  //@ diag(`Printf format %s has arg #1 of wrong type example.com/CheckPrintf.unexportedStringer`)\n\tfmt.Printf(\"%s\", &us) //@ diag(`Printf format %s has arg #1 of wrong type *example.com/CheckPrintf.unexportedStringer`)\n\n\tusf := unexportedStringerOtherFields{\n\t\ts: \"foo\",\n\t\tS: \"bar\",\n\t}\n\tfmt.Printf(\"%s\", usf)  //@ diag(`Printf format %s has arg #1 of wrong type example.com/CheckPrintf.unexportedStringerOtherFields`)\n\tfmt.Printf(\"%s\", &usf) //@ diag(`Printf format %s has arg #1 of wrong type *example.com/CheckPrintf.unexportedStringerOtherFields`)\n\n\tintSlice := []int{3, 4}\n\tfmt.Printf(\"%s\", intSlice) //@ diag(`Printf format %s has arg #1 of wrong type []int`)\n\tnonStringerArray := [1]unexportedStringer{{}}\n\tfmt.Printf(\"%s\", nonStringerArray)  //@ diag(`Printf format %s has arg #1 of wrong type [1]example.com/CheckPrintf.unexportedStringer`)\n\tfmt.Printf(\"%s\", []stringer{3, 4})  // not an error\n\tfmt.Printf(\"%s\", [2]stringer{3, 4}) // not an error\n}\n\n// TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11.\n// See issues 23598 and 23605.\nfunc DisableErrorForFlag0() {\n\tfmt.Printf(\"%0t\", true)\n}\n\n// Issue 26486.\nfunc dbg(format string, args ...interface{}) {\n\tif format == \"\" {\n\t\tformat = \"%v\"\n\t}\n\tfmt.Printf(format, args...)\n}\n\n// https://github.com/dominikh/go-tools/issues/714\nfunc fn2() {\n\ttype String string\n\ttype Byte byte\n\n\tvar a string = \"a\"\n\tvar b []byte = []byte{'b'}\n\tvar c [1]byte = [1]byte{'c'}\n\tvar d String = \"d\"\n\tvar e []uint8 = []uint8{'e'}\n\tvar f []Byte = []Byte{'h'}\n\tfmt.Printf(\"%s %s %s %s %s %s %s\", a, b, c, &c, d, e, f)\n}\n\nfunc fn3() {\n\ts := \"%d\"\n\tif true {\n\t\tfmt.Printf(s, \"\") //@ diag(`Printf format`)\n\t} else {\n\t\t_ = s\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa5010/sa5010.go",
    "content": "package sa5010\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA5010\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Impossible type assertion`,\n\n\t\tText: `Some type assertions can be statically proven to be\nimpossible. This is the case when the method sets of both\narguments of the type assertion conflict with each other, for\nexample by containing the same method with different\nsignatures.\n\nThe Go compiler already applies this check when asserting from an\ninterface value to a concrete type. If the concrete type misses\nmethods from the interface, or if function signatures don't match,\nthen the type assertion can never succeed.\n\nThis check applies the same logic when asserting from one interface to\nanother. If both interface types contain the same method but with\ndifferent signatures, then the type assertion can never succeed,\neither.`,\n\n\t\tSince:    \"2020.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\t// Technically this should be MergeIfAll, but the Go compiler\n\t\t// already flags some impossible type assertions, so\n\t\t// MergeIfAny is consistent with the compiler.\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\ttype entry struct {\n\t\tl, r *types.Func\n\t}\n\n\tmsc := &pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg.Prog.MethodSets\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tfor _, b := range fn.Blocks {\n\t\tinstrLoop:\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\tassert, ok := instr.(*ir.TypeAssert)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tvar wrong []entry\n\t\t\t\tleft := assert.X.Type()\n\t\t\t\tright := assert.AssertedType\n\t\t\t\trighti, ok := right.Underlying().(*types.Interface)\n\n\t\t\t\tif !ok {\n\t\t\t\t\t// We only care about interface->interface\n\t\t\t\t\t// assertions. The Go compiler already catches\n\t\t\t\t\t// impossible interface->concrete assertions.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tms := msc.MethodSet(left)\n\t\t\t\tfor mr := range righti.Methods() {\n\t\t\t\t\tsel := ms.Lookup(mr.Pkg(), mr.Name())\n\t\t\t\t\tif sel == nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tml := sel.Obj().(*types.Func)\n\t\t\t\t\tif ml.Origin() != ml || mr.Origin() != mr {\n\t\t\t\t\t\t// Give up when we see generics.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// TODO(dh): support generics once go/types gets an\n\t\t\t\t\t\t// exported API for type unification.\n\t\t\t\t\t\tcontinue instrLoop\n\t\t\t\t\t}\n\t\t\t\t\tif types.AssignableTo(ml.Type(), mr.Type()) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\twrong = append(wrong, entry{ml, mr})\n\t\t\t\t}\n\n\t\t\t\tif len(wrong) != 0 {\n\t\t\t\t\tvar s strings.Builder\n\t\t\t\t\ts.WriteString(fmt.Sprintf(\"impossible type assertion; %s and %s contradict each other:\",\n\t\t\t\t\t\ttypes.TypeString(left, types.RelativeTo(pass.Pkg)),\n\t\t\t\t\t\ttypes.TypeString(right, types.RelativeTo(pass.Pkg))))\n\t\t\t\t\tfor _, e := range wrong {\n\t\t\t\t\t\ts.WriteString(fmt.Sprintf(\"\\n\\twrong type for %s method\", e.l.Name()))\n\t\t\t\t\t\ts.WriteString(fmt.Sprintf(\"\\n\\t\\thave %s\", e.l.Type()))\n\t\t\t\t\t\ts.WriteString(fmt.Sprintf(\"\\n\\t\\twant %s\", e.r.Type()))\n\t\t\t\t\t}\n\t\t\t\t\treport.Report(pass, assert, s.String())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa5010/sa5010_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5010\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5010/testdata/go1.0/CheckImpossibleTypeAssertion/CheckImpossibleTypeAssertion.go",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype i1 interface {\n\tString() int\n}\n\ntype i2 interface {\n\tString() string\n}\n\ntype i3 interface {\n\tbar() int\n}\n\ntype i4 interface {\n\tString() int\n\tbar() int\n}\n\nfunc fn() {\n\tvar v1 i1\n\t_ = v1.(i2) //@ diag(`impossible type assertion; i1 and i2 contradict each other`)\n\t_ = v1.(i3)\n\t_ = v1.(i4)\n\t_ = v1.(fmt.Stringer) //@ diag(`impossible type assertion; i1 and fmt.Stringer contradict each other`)\n\t_ = v1.(interface {   //@ diag(re`i1 and.+String.+contradict each other`)\n\t\tString() string\n\t})\n}\n"
  },
  {
    "path": "staticcheck/sa5010/testdata/go1.18/CheckImpossibleTypeAssertion/CheckImpossibleTypeAssertion.go",
    "content": "package pkg\n\ntype ExampleType[T uint32 | uint64] interface {\n\tSomeMethod() T\n}\n\nfunc Fn1[T uint32 | uint64]() {\n\tvar iface ExampleType[uint32]\n\t_ = iface.(ExampleType[T])\n}\n\nfunc Fn2[T uint64]() {\n\tvar iface ExampleType[uint32]\n\t// TODO(dh): once we support generics, flag this\n\t_ = iface.(ExampleType[T])\n}\n\ntype I1[E any] interface {\n\tDo(E)\n\tMoo(E)\n}\n\ntype I2[E any] interface {\n\tDo(E)\n\tMoo(E)\n}\n\ntype I3[E any] interface {\n\tDo(E)\n\tMoo()\n}\n\nfunc New[T any]() {\n\tvar x I1[T]\n\t_ = x.(I2[T])\n\n\t// TODO(dh): once we support generics, flag this\n\t_ = x.(I3[T])\n}\n"
  },
  {
    "path": "staticcheck/sa5011/sa5011.go",
    "content": "package sa5011\n\nimport (\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA5011\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Possible nil pointer dereference`,\n\n\t\tText: `A pointer is being dereferenced unconditionally, while\nalso being checked against nil in another place. This suggests that\nthe pointer may be nil and dereferencing it may panic. This is\ncommonly a result of improperly ordered code or missing return\nstatements. Consider the following examples:\n\n    func fn(x *int) {\n        fmt.Println(*x)\n\n        // This nil check is equally important for the previous dereference\n        if x != nil {\n            foo(*x)\n        }\n    }\n\n    func TestFoo(t *testing.T) {\n        x := compute()\n        if x == nil {\n            t.Errorf(\"nil pointer received\")\n        }\n\n        // t.Errorf does not abort the test, so if x is nil, the next line will panic.\n        foo(*x)\n    }\n\nStaticcheck tries to deduce which functions abort control flow.\nFor example, it is aware that a function will not continue\nexecution after a call to \\'panic\\' or \\'log.Fatal\\'. However, sometimes\nthis detection fails, in particular in the presence of\nconditionals. Consider the following example:\n\n    func Log(msg string, level int) {\n        fmt.Println(msg)\n        if level == levelFatal {\n            os.Exit(1)\n        }\n    }\n\n    func Fatal(msg string) {\n        Log(msg, levelFatal)\n    }\n\n    func fn(x *int) {\n        if x == nil {\n            Fatal(\"unexpected nil pointer\")\n        }\n        fmt.Println(*x)\n    }\n\nStaticcheck will flag the dereference of \\'x\\', even though it is perfectly\nsafe. Staticcheck is not able to deduce that a call to\nFatal will exit the program. For the time being, the easiest\nworkaround is to modify the definition of Fatal like so:\n\n    func Fatal(msg string) {\n        Log(msg, levelFatal)\n        panic(\"unreachable\")\n    }\n\nWe also hard-code functions from common logging packages such as\nlogrus. Please file an issue if we're missing support for a\npopular package.`,\n\t\tSince:    \"2020.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// This is an extremely trivial check that doesn't try to reason\n\t// about control flow. That is, phis and sigmas do not propagate\n\t// any information. As such, we can flag this:\n\t//\n\t// \t_ = *x\n\t// \tif x == nil { return }\n\t//\n\t// but we cannot flag this:\n\t//\n\t// \tif x == nil { println(x) }\n\t// \t_ = *x\n\t//\n\t// but we can flag this, because the if's body doesn't use x:\n\t//\n\t// \tif x == nil { println(\"this is bad\") }\n\t//  _ = *x\n\t//\n\t// nor many other variations of conditional uses of or assignments to x.\n\t//\n\t// However, even this trivial implementation finds plenty of\n\t// real-world bugs, such as dereference before nil pointer check,\n\t// or using t.Error instead of t.Fatal when encountering nil\n\t// pointers.\n\t//\n\t// On the flip side, our naive implementation avoids false positives in branches, such as\n\t//\n\t// \tif x != nil { _ = *x }\n\t//\n\t// due to the same lack of propagating information through sigma\n\t// nodes. x inside the branch will be independent of the x in the\n\t// nil pointer check.\n\t//\n\t//\n\t// We could implement a more powerful check, but then we'd be\n\t// getting false positives instead of false negatives because\n\t// we're incapable of deducing relationships between variables.\n\t// For example, a function might return a pointer and an error,\n\t// and the error being nil guarantees that the pointer is not nil.\n\t// Depending on the surrounding code, the pointer may still end up\n\t// being checked against nil in one place, and guarded by a check\n\t// on the error in another, which would lead to us marking some\n\t// loads as unsafe.\n\t//\n\t// Unfortunately, simply hard-coding the relationship between\n\t// return values wouldn't eliminate all false positives, either.\n\t// Many other more subtle relationships exist. An abridged example\n\t// from real code:\n\t//\n\t// if a == nil && b == nil { return }\n\t// c := fn(a)\n\t// if c != \"\" { _ = *a }\n\t//\n\t// where `fn` is guaranteed to return a non-empty string if a\n\t// isn't nil.\n\t//\n\t// We choose to err on the side of false negatives.\n\n\tisNilConst := func(v ir.Value) bool {\n\t\tif typeutil.IsPointerLike(v.Type()) {\n\t\t\tif k, ok := v.(*ir.Const); ok {\n\t\t\t\treturn k.IsNil()\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tmaybeNil := map[ir.Value]ir.Instruction{}\n\t\tfor _, b := range fn.Blocks {\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\t// Originally we looked at all ir.BinOp, but that would lead to calls like 'assert(x != nil)' causing false positives.\n\t\t\t\t// Restrict ourselves to actual if statements, as these are more likely to affect control flow in a way we can observe.\n\t\t\t\tif instr, ok := instr.(*ir.If); ok {\n\t\t\t\t\tif cond, ok := instr.Cond.(*ir.BinOp); ok {\n\t\t\t\t\t\tif isNilConst(cond.X) {\n\t\t\t\t\t\t\tmaybeNil[cond.Y] = cond\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif isNilConst(cond.Y) {\n\t\t\t\t\t\t\tmaybeNil[cond.X] = cond\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor _, b := range fn.Blocks {\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\tvar ptr ir.Value\n\t\t\t\tswitch instr := instr.(type) {\n\t\t\t\tcase *ir.Load:\n\t\t\t\t\tptr = instr.X\n\t\t\t\tcase *ir.Store:\n\t\t\t\t\tptr = instr.Addr\n\t\t\t\tcase *ir.IndexAddr:\n\t\t\t\t\tptr = instr.X\n\t\t\t\t\tif typeutil.All(ptr.Type(), func(term *types.Term) bool {\n\t\t\t\t\t\tif _, ok := term.Type().Underlying().(*types.Slice); ok {\n\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}) {\n\t\t\t\t\t\t// indexing a nil slice does not cause a nil pointer panic\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// Note: This also works around the bad lowering of range loops over slices\n\t\t\t\t\t\t// (https://github.com/dominikh/go-tools/issues/1053)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\tcase *ir.FieldAddr:\n\t\t\t\t\tptr = instr.X\n\t\t\t\t}\n\t\t\t\tif ptr != nil {\n\t\t\t\t\tswitch ptr.(type) {\n\t\t\t\t\tcase *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr:\n\t\t\t\t\t\t// these cannot be nil\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif r, ok := maybeNil[ptr]; ok {\n\t\t\t\t\t\treport.Report(pass, instr, \"possible nil pointer dereference\",\n\t\t\t\t\t\t\treport.Related(r, \"this check suggests that the pointer can be nil\"))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa5011/sa5011_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5011\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5011/testdata/go1.0/CheckMaybeNil/CheckMaybeNil.go",
    "content": "package pkg\n\nimport (\n\t\"os\"\n\t\"syscall\"\n)\n\nfunc fn1(x *int) {\n\t_ = *x //@ diag(`possible nil pointer dereference`)\n\tif x != nil {\n\t\treturn\n\t}\n\tprintln()\n}\n\nfunc fn1_1(x *int) {\n\t// this doesn't get flagged because the conditional return gets optimized away\n\t_ = *x\n\tif x != nil {\n\t\treturn\n\t}\n}\n\nfunc fn2(x *int) {\n\tif x == nil {\n\t\tprintln(\"we should return\")\n\t}\n\t_ = *x //@ diag(`possible nil pointer dereference`)\n}\n\nfunc fn3(x *int) {\n\tif x != nil {\n\t\t_ = *x\n\t}\n}\n\nfunc fn4(x *int) {\n\tif x == nil {\n\t\tx = gen()\n\t}\n\t_ = *x\n}\n\nfunc fn5(x *int) {\n\tif x == nil {\n\t\tx = gen()\n\t}\n\t_ = *x //@ diag(`possible nil pointer dereference`)\n\tif x == nil {\n\t\tprintln(\"we should return\")\n\t}\n}\n\nfunc fn6() {\n\tx := new(int)\n\tif x == nil {\n\t\tprintln(\"we should return\")\n\t}\n\t// x can't be nil\n\t_ = *x\n}\n\nfunc fn7() {\n\tvar x int\n\ty := &x\n\tif y == nil {\n\t\tprintln(\"we should return\")\n\t}\n\t// y can't be nil\n\t_ = *y\n}\n\nfunc fn8(x *int) {\n\tif x == nil {\n\t\treturn\n\t}\n\t// x can't be nil\n\t_ = *x\n}\n\nfunc fn9(x *int) {\n\tif x != nil {\n\t\treturn\n\t}\n\t// TODO(dh): not currently supported\n\t_ = *x\n}\n\nfunc gen() *int { return nil }\n\nfunc die1(b bool) {\n\tif b {\n\t\tprintln(\"yay\")\n\t\tos.Exit(0)\n\t} else {\n\t\tprintln(\"nay\")\n\t\tos.Exit(1)\n\t}\n}\n\nfunc die2(b bool) {\n\tif b {\n\t\tprintln(\"yay\")\n\t\tos.Exit(0)\n\t}\n}\n\nfunc fn10(x *int) {\n\tif x == nil {\n\t\tdie1(true)\n\t}\n\t_ = *x\n}\n\nfunc fn11(x *int) {\n\tif x == nil {\n\t\tdie2(true)\n\t}\n\t_ = *x //@ diag(`possible nil pointer dereference`)\n}\n\nfunc doPanic() { panic(\"\") }\nfunc doExit()  { syscall.Exit(1) }\n\nfunc fn12(arg bool) {\n\tif arg {\n\t\tdoPanic()\n\t} else {\n\t\tdoExit()\n\t}\n}\n\nfunc fn13(arg bool) {\n\tfn12(arg)\n}\n\nfunc fn14(x *int) {\n\tif x == nil {\n\t\tfn13(true)\n\t}\n\t_ = *x\n}\n\nfunc assert(b bool) {\n\tif b {\n\t\tpanic(\"meh\")\n\t}\n}\n\nfunc fn15(x *int) {\n\tassert(x != nil)\n\t_ = *x\n}\n\nfunc fn16() {\n\tvar xs []int\n\tif xs == nil {\n\t\tprintln()\n\t}\n\n\tfor _, x := range xs {\n\t\t_ = x\n\t}\n\n\tvar xs2 *[1]int\n\tif xs2 == nil {\n\t\tprintln()\n\t}\n\t// this used to get flagged, but now that we correctly insert sigma nodes for range loops, we can no longer flag this\n\tfor _, x := range xs2 {\n\t\t_ = x\n\t}\n\n\tvar xs3 *[]int\n\tif xs3 == nil {\n\t\tprintln()\n\t}\n\tfor _, x := range *xs3 { //@ diag(`possible nil pointer dereference`)\n\t\t_ = x\n\t}\n\n\tvar xs4 []int\n\tif xs4 == nil {\n\t\tprintln()\n\t}\n\t_ = xs4[0]\n}\n"
  },
  {
    "path": "staticcheck/sa5011/testdata/go1.18/CheckMaybeNil/generics.go",
    "content": "package pkg\n\nfunc tpfn1[T []int](x T) {\n\t// don't flag, T is a slice\n\t_ = x[0]\n\tif x == nil {\n\t\treturn\n\t}\n\tprintln()\n}\n\nfunc tpfn2[T *int,](x T) {\n\t_ = *x //@ diag(`possible nil pointer dereference`)\n\tif x == nil {\n\t\treturn\n\t}\n\tprintln()\n}\n"
  },
  {
    "path": "staticcheck/sa5012/sa5012.go",
    "content": "package sa5012\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:      \"SA5012\",\n\t\tRun:       run,\n\t\tFactTypes: []analysis.Fact{new(evenElements)},\n\t\tRequires:  []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Passing odd-sized slice to function expecting even size\",\n\t\tText: `Some functions that take slices as parameters expect the slices to have an even number of elements. \nOften, these functions treat elements in a slice as pairs. \nFor example, \\'strings.NewReplacer\\' takes pairs of old and new strings, \nand calling it with an odd number of elements would be an error.`,\n\t\tSince:    \"2020.2\",\n\t\tSeverity: lint.SeverityError,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\ntype evenElements struct{}\n\nfunc (evenElements) AFact() {}\n\nfunc (evenElements) String() string { return \"needs even elements\" }\n\nfunc findSliceLength(v ir.Value) int {\n\t// TODO(dh): VRP would help here\n\n\tv = irutil.Flatten(v)\n\tval := func(v ir.Value) int {\n\t\tif v, ok := v.(*ir.Const); ok {\n\t\t\treturn int(v.Int64())\n\t\t}\n\t\treturn -1\n\t}\n\tswitch v := v.(type) {\n\tcase *ir.Slice:\n\t\tlow := 0\n\t\thigh := -1\n\t\tif v.Low != nil {\n\t\t\tlow = val(v.Low)\n\t\t}\n\t\tif v.High != nil {\n\t\t\thigh = val(v.High)\n\t\t} else {\n\t\t\tswitch vv := v.X.(type) {\n\t\t\tcase *ir.Alloc:\n\t\t\t\thigh = int(typeutil.Dereference(vv.Type()).Underlying().(*types.Array).Len())\n\t\t\tcase *ir.Slice:\n\t\t\t\thigh = findSliceLength(vv)\n\t\t\t}\n\t\t}\n\t\tif low == -1 || high == -1 {\n\t\t\treturn -1\n\t\t}\n\t\treturn high - low\n\tdefault:\n\t\treturn -1\n\t}\n}\n\nfunc flagSliceLens(pass *analysis.Pass) {\n\tvar tag evenElements\n\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tfor _, b := range fn.Blocks {\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\tcall, ok := instr.(ir.CallInstruction)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcallee := call.Common().StaticCallee()\n\t\t\t\tif callee == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfor argi, arg := range call.Common().Args {\n\t\t\t\t\tif callee.Signature.Recv() != nil {\n\t\t\t\t\t\tif argi == 0 {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\targi--\n\t\t\t\t\t}\n\n\t\t\t\t\t_, ok := arg.Type().Underlying().(*types.Slice)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tparam := callee.Signature.Params().At(argi)\n\t\t\t\t\tif !pass.ImportObjectFact(param, &tag) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// TODO handle stubs\n\n\t\t\t\t\t// we know the argument has to have even length.\n\t\t\t\t\t// now let's try to find its length\n\t\t\t\t\tif n := findSliceLength(arg); n > -1 && n%2 != 0 {\n\t\t\t\t\t\tsrc := call.Source().(*ast.CallExpr).Args[argi]\n\t\t\t\t\t\tsig := call.Common().Signature()\n\t\t\t\t\t\tvar label string\n\t\t\t\t\t\tif argi == sig.Params().Len()-1 && sig.Variadic() {\n\t\t\t\t\t\t\tlabel = \"variadic argument\"\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlabel = \"argument\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Note that param.Name() is guaranteed to not\n\t\t\t\t\t\t// be empty, otherwise the function couldn't\n\t\t\t\t\t\t// have enforced its length.\n\t\t\t\t\t\treport.Report(pass, src, fmt.Sprintf(\"%s %q is expected to have even number of elements, but has %d elements\", label, param.Name(), n))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc findSliceLenChecks(pass *analysis.Pass) {\n\t// mark all function parameters that have to be of even length\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tfor _, b := range fn.Blocks {\n\t\t\t// all paths go through this block\n\t\t\tif !b.Dominates(fn.Exit) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// if foo % 2 != 0\n\t\t\tifi, ok := b.Control().(*ir.If)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcmp, ok := ifi.Cond.(*ir.BinOp)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar needle uint64\n\t\t\tswitch cmp.Op {\n\t\t\tcase token.NEQ:\n\t\t\t\t// look for != 0\n\t\t\t\tneedle = 0\n\t\t\tcase token.EQL:\n\t\t\t\t// look for == 1\n\t\t\t\tneedle = 1\n\t\t\tdefault:\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\trem, ok1 := cmp.X.(*ir.BinOp)\n\t\t\tk, ok2 := cmp.Y.(*ir.Const)\n\t\t\tif ok1 != ok2 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif !ok1 {\n\t\t\t\trem, ok1 = cmp.Y.(*ir.BinOp)\n\t\t\t\tk, ok2 = cmp.X.(*ir.Const)\n\t\t\t}\n\t\t\tif !ok1 || !ok2 || rem.Op != token.REM || k.Value.Kind() != constant.Int || k.Uint64() != needle {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tk, ok = rem.Y.(*ir.Const)\n\t\t\tif !ok || k.Value.Kind() != constant.Int || k.Uint64() != 2 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// if len(foo) % 2 != 0\n\t\t\tcall, ok := rem.X.(*ir.Call)\n\t\t\tif !ok || !irutil.IsCallTo(call.Common(), \"len\") {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// we're checking the length of a parameter that is a slice\n\t\t\t// TODO(dh): support parameters that have flown through sigmas and phis\n\t\t\tparam, ok := call.Call.Args[0].(*ir.Parameter)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif !typeutil.All(param.Type(), typeutil.IsSlice) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// if len(foo) % 2 != 0 then panic\n\t\t\tif _, ok := b.Succs[0].Control().(*ir.Panic); !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tpass.ExportObjectFact(param.Object(), new(evenElements))\n\t\t}\n\t}\n}\n\nfunc findIndirectSliceLenChecks(pass *analysis.Pass) {\n\tseen := map[*ir.Function]struct{}{}\n\n\tvar doFunction func(fn *ir.Function)\n\tdoFunction = func(fn *ir.Function) {\n\t\tif _, ok := seen[fn]; ok {\n\t\t\treturn\n\t\t}\n\t\tseen[fn] = struct{}{}\n\n\t\tfor _, b := range fn.Blocks {\n\t\t\t// all paths go through this block\n\t\t\tif !b.Dominates(fn.Exit) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\tcall, ok := instr.(*ir.Call)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcallee := call.Call.StaticCallee()\n\t\t\t\tif callee == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif callee.Pkg == fn.Pkg || callee.Pkg == nil {\n\t\t\t\t\tdoFunction(callee)\n\t\t\t\t}\n\n\t\t\t\tfor argi, arg := range call.Call.Args {\n\t\t\t\t\tif callee.Signature.Recv() != nil {\n\t\t\t\t\t\tif argi == 0 {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\targi--\n\t\t\t\t\t}\n\n\t\t\t\t\t// TODO(dh): support parameters that have flown through length-preserving instructions\n\t\t\t\t\tparam, ok := arg.(*ir.Parameter)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif !typeutil.All(param.Type(), typeutil.IsSlice) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// We can't use callee.Params to look up the\n\t\t\t\t\t// parameter, because Params is not populated for\n\t\t\t\t\t// external functions. In our modular analysis.\n\t\t\t\t\t// any function in any package that isn't the\n\t\t\t\t\t// current package is considered \"external\", as it\n\t\t\t\t\t// has been loaded from export data only.\n\t\t\t\t\tsigParams := callee.Signature.Params()\n\n\t\t\t\t\tif !pass.ImportObjectFact(sigParams.At(argi), new(evenElements)) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tpass.ExportObjectFact(param.Object(), new(evenElements))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tdoFunction(fn)\n\t}\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfindSliceLenChecks(pass)\n\tfindIndirectSliceLenChecks(pass)\n\tflagSliceLens(pass)\n\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa5012/sa5012_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa5012\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa5012/testdata/go1.0/CheckEvenSliceLength/CheckEvenSliceLength.go",
    "content": "package pkg\n\nimport \"strings\"\n\nfunc fnVariadic(s string, args ...interface{}) { //@ fact(args, \"needs even elements\")\n\tif len(args)%2 != 0 {\n\t\tpanic(\"I'm one of those annoying logging APIs\")\n\t}\n}\n\nfunc fnSlice(s string, args []interface{}) { //@ fact(args, \"needs even elements\")\n\tif len(args)%2 != 0 {\n\t\tpanic(\"I'm one of those annoying logging APIs\")\n\t}\n}\n\nfunc fnIndirect(s string, args ...interface{}) { //@ fact(args, \"needs even elements\")\n\tfnSlice(s, args)\n}\n\nfunc fn2(bleh []interface{}, arr1 [3]interface{}) { //@ fact(bleh, \"needs even elements\")\n\tfnVariadic(\"%s\", 1, 2, 3) //@ diag(re`variadic argument \"args\".+ but has 3 elements`)\n\targs := []interface{}{1, 2, 3}\n\tfnVariadic(\"\", args...)     //@ diag(re`variadic argument \"args\".+ but has 3 elements`)\n\tfnVariadic(\"\", args[:1]...) //@ diag(re`variadic argument \"args\".+ but has 1 elements`)\n\tfnVariadic(\"\", args[:2]...)\n\tfnVariadic(\"\", args[0:1]...) //@ diag(re`variadic argument \"args\".+ but has 1 elements`)\n\tfnVariadic(\"\", args[0:]...)  //@ diag(re`variadic argument \"args\".+ but has 3 elements`)\n\tfnVariadic(\"\", args[:]...)   //@ diag(re`variadic argument \"args\".+ but has 3 elements`)\n\tfnVariadic(\"\", bleh...)\n\tfnVariadic(\"\", bleh[:1]...)  //@ diag(re`variadic argument \"args\".+ but has 1 elements`)\n\tfnVariadic(\"\", bleh[0:1]...) //@ diag(re`variadic argument \"args\".+ but has 1 elements`)\n\tfnVariadic(\"\", bleh[0:]...)\n\tfnVariadic(\"\", bleh[:]...)\n\tfnVariadic(\"\", bleh)                      //@ diag(re`variadic argument \"args\".+ but has 1 elements`)\n\tfnVariadic(\"\", make([]interface{}, 3)...) //@ diag(re`variadic argument \"args\".+ but has 3 elements`)\n\tfnVariadic(\"\", make([]interface{}, 4)...)\n\tvar arr2 [3]interface{}\n\tfnVariadic(\"\", arr1[:]...) //@ diag(re`variadic argument \"args\".+ but has 3 elements`)\n\tfnVariadic(\"\", arr2[:]...) //@ diag(re`variadic argument \"args\".+ but has 3 elements`)\n\n\tfnSlice(\"\", []interface{}{1, 2, 3}) //@ diag(re`argument \"args\".+ but has 3 elements`)\n\tfnSlice(\"\", []interface{}{1, 2, 3, 4})\n\n\tfnIndirect(\"%s\", 1, 2, 3) //@ diag(re`argument \"args\".+ but has 3 elements`)\n\tfnIndirect(\"%s\", 1, 2)\n\n\tstrings.NewReplacer(\"one\") //@ diag(re`variadic argument \"oldnew\".+ but has 1 elements`)\n\tstrings.NewReplacer(\"one\", \"two\")\n}\n\nfunc fn3() {\n\targs := []interface{}{\"\"}\n\tif true {\n\t\tfnSlice(\"\", args) //@ diag(`but has 1 element`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa6000/sa6000.go",
    "content": "package sa6000\n\nimport (\n\t\"fmt\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA6000\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Using \\'regexp.Match\\' or related in a loop, should use \\'regexp.Compile\\'`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"regexp.Match\":       check(\"regexp.Match\"),\n\t\"regexp.MatchReader\": check(\"regexp.MatchReader\"),\n\t\"regexp.MatchString\": check(\"regexp.MatchString\"),\n}\n\nfunc check(name string) callcheck.Check {\n\treturn func(call *callcheck.Call) {\n\t\tif callcheck.ExtractConst(call.Args[0].Value) == nil {\n\t\t\treturn\n\t\t}\n\t\tif !isInLoop(call.Instr.Block()) {\n\t\t\treturn\n\t\t}\n\t\tcall.Invalid(fmt.Sprintf(\"calling %s in a loop has poor performance, consider using regexp.Compile\", name))\n\t}\n}\n\nfunc isInLoop(b *ir.BasicBlock) bool {\n\tsets := irutil.FindLoops(b.Parent())\n\tfor _, set := range sets {\n\t\tif set.Has(b) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "staticcheck/sa6000/sa6000_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa6000\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa6000/testdata/go1.0/CheckRegexpMatchLoop/CheckRegexpMatchLoop.go",
    "content": "package pkg\n\nimport \"regexp\"\n\nfunc fn() {\n\tregexp.Match(\".\", nil)\n\tregexp.MatchString(\".\", \"\")\n\tregexp.MatchReader(\".\", nil)\n\n\tfor {\n\t\tregexp.Match(\".\", nil)       //@ diag(`calling regexp.Match in a loop has poor performance`)\n\t\tregexp.MatchString(\".\", \"\")  //@ diag(`calling regexp.MatchString in a loop has poor performance`)\n\t\tregexp.MatchReader(\".\", nil) //@ diag(`calling regexp.MatchReader in a loop has poor performance`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa6001/sa6001.go",
    "content": "package sa6001\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA6001\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Missing an optimization opportunity when indexing maps by byte slices`,\n\n\t\tText: `Map keys must be comparable, which precludes the use of byte slices.\nThis usually leads to using string keys and converting byte slices to\nstrings.\n\nNormally, a conversion of a byte slice to a string needs to copy the data and\ncauses allocations. The compiler, however, recognizes \\'m[string(b)]\\' and\nuses the data of \\'b\\' directly, without copying it, because it knows that\nthe data can't change during the map lookup. This leads to the\ncounter-intuitive situation that\n\n    k := string(b)\n    println(m[k])\n    println(m[k])\n\nwill be less efficient than\n\n    println(m[string(b)])\n    println(m[string(b)])\n\nbecause the first version needs to copy and allocate, while the second\none does not.\n\nFor some history on this optimization, check out commit\nf5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tfor _, b := range fn.Blocks {\n\t\tinsLoop:\n\t\t\tfor _, ins := range b.Instrs {\n\t\t\t\tvar fromType types.Type\n\t\t\t\tvar toType types.Type\n\n\t\t\t\t// find []byte -> string conversions\n\t\t\t\tswitch ins := ins.(type) {\n\t\t\t\tcase *ir.Convert:\n\t\t\t\t\tfromType = ins.X.Type()\n\t\t\t\t\ttoType = ins.Type()\n\t\t\t\tcase *ir.MultiConvert:\n\t\t\t\t\tfromType = ins.X.Type()\n\t\t\t\t\ttoType = ins.Type()\n\t\t\t\tdefault:\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif toType != types.Universe.Lookup(\"string\").Type() {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\ttset := typeutil.NewTypeSet(fromType)\n\t\t\t\t// If at least one of the types is []byte, then it's more efficient to inline the conversion\n\t\t\t\tif !tset.Any(func(term *types.Term) bool {\n\t\t\t\t\ts, ok := term.Type().Underlying().(*types.Slice)\n\t\t\t\t\treturn ok && s.Elem().Underlying() == types.Universe.Lookup(\"byte\").Type()\n\t\t\t\t}) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\trefs := ins.Referrers()\n\t\t\t\t// need at least two (DebugRef) references: the\n\t\t\t\t// conversion and the *ast.Ident\n\t\t\t\tif refs == nil || len(*refs) < 2 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tident := false\n\t\t\t\t// skip first reference, that's the conversion itself\n\t\t\t\tfor _, ref := range (*refs)[1:] {\n\t\t\t\t\tswitch ref := ref.(type) {\n\t\t\t\t\tcase *ir.DebugRef:\n\t\t\t\t\t\tif _, ok := ref.Expr.(*ast.Ident); !ok {\n\t\t\t\t\t\t\t// the string seems to be used somewhere\n\t\t\t\t\t\t\t// unexpected; the default branch should\n\t\t\t\t\t\t\t// catch this already, but be safe\n\t\t\t\t\t\t\tcontinue insLoop\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tident = true\n\t\t\t\t\t\t}\n\t\t\t\t\tcase *ir.MapLookup:\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t// the string is used somewhere else than a\n\t\t\t\t\t\t// map lookup\n\t\t\t\t\t\tcontinue insLoop\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// the result of the conversion wasn't assigned to an\n\t\t\t\t// identifier\n\t\t\t\tif !ident {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treport.Report(pass, ins, \"m[string(key)] would be more efficient than k := string(key); m[k]\")\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa6001/sa6001_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa6001\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa6001/testdata/go1.0/CheckMapBytesKey/key.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar m map[string]int\n\tvar b []byte\n\t_ = m[string(b)]\n\t_ = m[string(b)]\n\ts1 := string(b) //@ diag(`m[string(key)] would be more efficient than k := string(key); m[k]`)\n\t_ = m[s1]\n\t_ = m[s1]\n\n\ts2 := string(b)\n\t_ = m[s2]\n\t_ = m[s2]\n\tprintln(s2)\n}\n"
  },
  {
    "path": "staticcheck/sa6001/testdata/go1.18/CheckMapBytesKey/key_generics.go",
    "content": "package pkg\n\nfunc tpfn[T ~string | []byte | int](b T) {\n\tvar m map[string]int\n\tk := string(b) //@ diag(`would be more efficient`)\n\t_ = m[k]\n}\n"
  },
  {
    "path": "staticcheck/sa6002/sa6002.go",
    "content": "package sa6002\n\nimport (\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA6002\",\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t\tRun:      callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Storing non-pointer values in \\'sync.Pool\\' allocates memory`,\n\t\tText: `A \\'sync.Pool\\' is used to avoid unnecessary allocations and reduce the\namount of work the garbage collector has to do.\n\nWhen passing a value that is not a pointer to a function that accepts\nan interface, the value needs to be placed on the heap, which means an\nadditional allocation. Slices are a common thing to put in sync.Pools,\nand they're structs with 3 fields (length, capacity, and a pointer to\nan array). In order to avoid the extra allocation, one should store a\npointer to the slice instead.\n\nSee the comments on https://go-review.googlesource.com/c/go/+/24371\nthat discuss this problem.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t\"(*sync.Pool).Put\": func(call *callcheck.Call) {\n\t\targ := call.Args[knowledge.Arg(\"(*sync.Pool).Put.x\")]\n\t\ttyp := arg.Value.Value.Type()\n\t\t_, isSlice := typ.Underlying().(*types.Slice)\n\t\tif !typeutil.IsPointerLike(typ) || isSlice {\n\t\t\targ.Invalid(\"argument should be pointer-like to avoid allocations\")\n\t\t}\n\t},\n}\n"
  },
  {
    "path": "staticcheck/sa6002/sa6002_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa6002\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa6002/testdata/go1.0/CheckSyncPoolValue/CheckSyncPoolValue.go",
    "content": "package pkg\n\nimport (\n\t\"sync\"\n\t\"unsafe\"\n)\n\ntype T1 struct {\n\tx int\n}\n\ntype T2 struct {\n\tx int\n\ty int\n}\n\nfunc fn() {\n\ts := []int{}\n\n\tv := sync.Pool{}\n\tv.Put(s) //@ diag(`argument should be pointer-like`)\n\tv.Put(&s)\n\tv.Put(T1{}) //@ diag(`argument should be pointer-like`)\n\tv.Put(T2{}) //@ diag(`argument should be pointer-like`)\n\n\tp := &sync.Pool{}\n\tp.Put(s) //@ diag(`argument should be pointer-like`)\n\tp.Put(&s)\n\n\tvar i interface{}\n\tp.Put(i)\n\n\tvar up unsafe.Pointer\n\tp.Put(up)\n\n\tvar basic int\n\tp.Put(basic) //@ diag(`argument should be pointer-like`)\n}\n\nfunc fn2() {\n\t// https://github.com/dominikh/go-tools/issues/873\n\tvar pool sync.Pool\n\tfunc() {\n\t\tdefer pool.Put([]byte{}) //@ diag(`argument should be pointer-like`)\n\t}()\n}\n\nfunc fn3() {\n\tvar pool sync.Pool\n\tdefer pool.Put([]byte{}) //@ diag(`argument should be pointer-like`)\n\tgo pool.Put([]byte{})    //@ diag(`argument should be pointer-like`)\n}\n"
  },
  {
    "path": "staticcheck/sa6003/sa6003.go",
    "content": "package sa6003\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/internal/sharedcheck\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA6003\",\n\t\tRun:      sharedcheck.CheckRangeStringRunes,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Converting a string to a slice of runes before ranging over it`,\n\t\tText: `You may want to loop over the runes in a string. Instead of converting\nthe string to a slice of runes and looping over that, you can loop\nover the string itself. That is,\n\n    for _, r := range s {}\n\nand\n\n    for _, r := range []rune(s) {}\n\nwill yield the same values. The first version, however, will be faster\nand avoid unnecessary memory allocations.\n\nDo note that if you are interested in the indices, ranging over a\nstring and over a slice of runes will yield different indices. The\nfirst one yields byte offsets, while the second one yields indices in\nthe slice of runes.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n"
  },
  {
    "path": "staticcheck/sa6003/sa6003_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa6003\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa6003/testdata/go1.0/CheckRangeStringRunes/CheckRangeStringRunes.go",
    "content": "package pkg\n\ntype String string\n\nfunc fn(s string, s2 String) {\n\tfor _, r := range s {\n\t\tprintln(r)\n\t}\n\n\tfor _, r := range []rune(s) { //@ diag(`should range over string`)\n\t\tprintln(r)\n\t}\n\n\tfor i, r := range []rune(s) {\n\t\tprintln(i)\n\t\tprintln(r)\n\t}\n\n\tx := []rune(s)\n\tfor _, r := range x { //@ diag(`should range over string`)\n\t\tprintln(r)\n\t}\n\n\ty := []rune(s)\n\tfor _, r := range y {\n\t\tprintln(r)\n\t}\n\tprintln(y[0])\n\n\tfor _, r := range []rune(s2) { //@ diag(`should range over string`)\n\t\tprintln(r)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa6003/testdata/go1.18/CheckRangeStringRunes/generics.go",
    "content": "package pkg\n\nfunc tpfn1[T string](x T) {\n\tfor _, c := range []rune(x) { //@ diag(`should range over string`)\n\t\tprintln(c)\n\t}\n}\n\nfunc tpfn2[T1 string, T2 []rune](x T1) {\n\tfor _, c := range T2(x) { //@ diag(`should range over string`)\n\t\tprintln(c)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa6005/sa6005.go",
    "content": "package sa6005\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA6005\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Inefficient string comparison with \\'strings.ToLower\\' or \\'strings.ToUpper\\'`,\n\t\tText: `Converting two strings to the same case and comparing them like so\n\n    if strings.ToLower(s1) == strings.ToLower(s2) {\n        ...\n    }\n\nis significantly more expensive than comparing them with\n\\'strings.EqualFold(s1, s2)\\'. This is due to memory usage as well as\ncomputational complexity.\n\n\\'strings.ToLower\\' will have to allocate memory for the new strings, as\nwell as convert both strings fully, even if they differ on the very\nfirst byte. strings.EqualFold, on the other hand, compares the strings\none character at a time. It doesn't need to create two intermediate\nstrings and can return as soon as the first non-matching character has\nbeen found.\n\nFor a more in-depth explanation of this issue, see\nhttps://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/`,\n\t\tSince:    \"2019.2\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckToLowerToUpperComparisonQ = pattern.MustParse(`\n\t(BinaryExpr\n\t\t(CallExpr fun@(Symbol (Or \"strings.ToLower\" \"strings.ToUpper\")) [a])\n \t\ttok@(Or \"==\" \"!=\")\n \t\t(CallExpr fun [b]))`)\n\tcheckToLowerToUpperComparisonR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident \"strings\") (Ident \"EqualFold\")) [a b])`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkToLowerToUpperComparisonQ) {\n\t\trn := pattern.NodeToAST(checkToLowerToUpperComparisonR.Root, m.State).(ast.Expr)\n\t\tmethod := \"strings.EqualFold\"\n\t\tif m.State[\"tok\"].(token.Token) == token.NEQ {\n\t\t\trn = &ast.UnaryExpr{\n\t\t\t\tOp: token.NOT,\n\t\t\t\tX:  rn,\n\t\t\t}\n\t\t\tmethod = \"!\" + method\n\t\t}\n\n\t\treport.Report(pass, node,\n\t\t\tfmt.Sprintf(\"should use %s instead\", method),\n\t\t\treport.Fixes(edit.Fix(\"replace with \"+method, edit.ReplaceWithNode(pass.Fset, node, rn))))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa6005/sa6005_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa6005\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa6005/testdata/go1.0/CheckToLowerToUpperComparison/CheckToLowerToUpperComparison.go",
    "content": "package pkg\n\nimport \"strings\"\n\nfunc fn() {\n\tconst (\n\t\ts1 = \"foo\"\n\t\ts2 = \"bar\"\n\t)\n\n\tif strings.ToLower(s1) == strings.ToLower(s2) { //@ diag(`should use strings.EqualFold instead`)\n\t\tpanic(\"\")\n\t}\n\n\tif strings.ToUpper(s1) == strings.ToUpper(s2) { //@ diag(`should use strings.EqualFold instead`)\n\t\tpanic(\"\")\n\t}\n\n\tif strings.ToLower(s1) != strings.ToLower(s2) { //@ diag(`should use !strings.EqualFold instead`)\n\t\tpanic(\"\")\n\t}\n\n\tswitch strings.ToLower(s1) == strings.ToLower(s2) { //@ diag(`should use strings.EqualFold instead`)\n\tcase true, false:\n\t\tpanic(\"\")\n\t}\n\n\tif strings.ToLower(s1) == strings.ToLower(s2) || s1+s2 == s2+s1 { //@ diag(`should use strings.EqualFold instead`)\n\t\tpanic(\"\")\n\t}\n\n\tif strings.ToLower(s1) > strings.ToLower(s2) {\n\t\tpanic(\"\")\n\t}\n\n\tif strings.ToLower(s1) < strings.ToLower(s2) {\n\t\tpanic(\"\")\n\t}\n\n\tif strings.ToLower(s1) == strings.ToUpper(s2) {\n\t\tpanic(\"\")\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa6005/testdata/go1.0/CheckToLowerToUpperComparison/CheckToLowerToUpperComparison.go.golden",
    "content": "package pkg\n\nimport \"strings\"\n\nfunc fn() {\n\tconst (\n\t\ts1 = \"foo\"\n\t\ts2 = \"bar\"\n\t)\n\n\tif strings.EqualFold(s1, s2) { //@ diag(`should use strings.EqualFold instead`)\n\t\tpanic(\"\")\n\t}\n\n\tif strings.EqualFold(s1, s2) { //@ diag(`should use strings.EqualFold instead`)\n\t\tpanic(\"\")\n\t}\n\n\tif !strings.EqualFold(s1, s2) { //@ diag(`should use !strings.EqualFold instead`)\n\t\tpanic(\"\")\n\t}\n\n\tswitch strings.EqualFold(s1, s2) { //@ diag(`should use strings.EqualFold instead`)\n\tcase true, false:\n\t\tpanic(\"\")\n\t}\n\n\tif strings.EqualFold(s1, s2) || s1+s2 == s2+s1 { //@ diag(`should use strings.EqualFold instead`)\n\t\tpanic(\"\")\n\t}\n\n\tif strings.ToLower(s1) > strings.ToLower(s2) {\n\t\tpanic(\"\")\n\t}\n\n\tif strings.ToLower(s1) < strings.ToLower(s2) {\n\t\tpanic(\"\")\n\t}\n\n\tif strings.ToLower(s1) == strings.ToUpper(s2) {\n\t\tpanic(\"\")\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa6006/sa6006.go",
    "content": "package sa6006\n\nimport (\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA6006\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Using io.WriteString to write \\'[]byte\\'`,\n\t\tText: `Using io.WriteString to write a slice of bytes, as in\n\n    io.WriteString(w, string(b))\n\nis both unnecessary and inefficient. Converting from \\'[]byte\\' to \\'string\\'\nhas to allocate and copy the data, and we could simply use \\'w.Write(b)\\'\ninstead.`,\n\n\t\tSince: \"2024.1\",\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar ioWriteStringConversion = pattern.MustParse(`(CallExpr (Symbol \"io.WriteString\") [_ (CallExpr (Builtin \"string\") [arg])])`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, ioWriteStringConversion) {\n\t\tif !code.IsOfStringConvertibleByteSlice(pass, m.State[\"arg\"].(ast.Expr)) {\n\t\t\tcontinue\n\t\t}\n\t\treport.Report(pass, node, \"use io.Writer.Write instead of converting from []byte to string to use io.WriteString\")\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa6006/sa6006_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa6006\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa6006/testdata/go1.0/CheckByteSliceInIOWriteString/CheckByteSliceInIOWriteString.go",
    "content": "package pkg\n\nimport (\n\t\"io\"\n)\n\nfunc f() {\n\tvar b []byte\n\tio.WriteString(nil, string(b)) //@ diag(`use io.Writer.Write`)\n\n\ttype custom []byte\n\tvar c custom\n\tio.WriteString(nil, string(c)) //@ diag(`use io.Writer.Write`)\n\n\tg := func() []byte { return nil }\n\tio.WriteString(nil, string(g())) //@ diag(`use io.Writer.Write`)\n\n\tvar d string\n\tio.WriteString(nil, d)\n\n\tio.WriteString(nil, string(123))\n\n\tstring := func(x []byte) string { return \"\" }\n\tio.WriteString(nil, string(b))\n}\n"
  },
  {
    "path": "staticcheck/sa9001/sa9001.go",
    "content": "package sa9001\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA9001\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Defers in range loops may not run when you expect them to`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tloop := node.(*ast.RangeStmt)\n\t\ttyp := pass.TypesInfo.TypeOf(loop.X)\n\t\t_, ok := typeutil.CoreType(typ).(*types.Chan)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tstmts := []*ast.DeferStmt{}\n\t\texits := false\n\t\tfn2 := func(node ast.Node) bool {\n\t\t\tswitch stmt := node.(type) {\n\t\t\tcase *ast.DeferStmt:\n\t\t\t\tstmts = append(stmts, stmt)\n\t\t\tcase *ast.FuncLit:\n\t\t\t\t// Don't look into function bodies\n\t\t\t\treturn false\n\t\t\tcase *ast.ReturnStmt:\n\t\t\t\texits = true\n\t\t\tcase *ast.BranchStmt:\n\t\t\t\texits = node.(*ast.BranchStmt).Tok == token.BREAK\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t\tast.Inspect(loop.Body, fn2)\n\n\t\tif exits {\n\t\t\treturn\n\t\t}\n\t\tfor _, stmt := range stmts {\n\t\t\treport.Report(pass, stmt, \"defers in this range loop won't run unless the channel gets closed\")\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.RangeStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa9001/sa9001_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa9001\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa9001/testdata/go1.0/CheckDubiousDeferInChannelRangeLoop/CheckDubiousDeferInChannelRangeLoop.go",
    "content": "package pkg\n\nfunc fn() {\n\tvar ch chan int\n\tfor range ch {\n\t\tdefer println() //@ diag(`defers in this range loop`)\n\t}\n}\n\nfunc fn2() {\n\tvar ch chan int\n\tfor range ch {\n\t\tdefer println()\n\t\tbreak\n\t}\n\n\tfor range ch {\n\t\tdefer println()\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa9001/testdata/go1.18/CheckDubiousDeferInChannelRangeLoop/generics.go",
    "content": "package pkg\n\nfunc tpfn[T chan int]() {\n\tvar ch T\n\tfor range ch {\n\t\tdefer println() //@ diag(`defers in this range loop`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa9002/sa9002.go",
    "content": "package sa9002\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"strconv\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA9002\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:    `Using a non-octal \\'os.FileMode\\' that looks like it was meant to be in octal.`,\n\t\tSince:    \"2017.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tcall := node.(*ast.CallExpr)\n\t\tfor _, arg := range call.Args {\n\t\t\tlit, ok := arg.(*ast.BasicLit)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif !typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(lit), \"os.FileMode\") &&\n\t\t\t\t!typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(lit), \"io/fs.FileMode\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif len(lit.Value) == 3 &&\n\t\t\t\tlit.Value[0] != '0' &&\n\t\t\t\tlit.Value[0] >= '0' && lit.Value[0] <= '7' &&\n\t\t\t\tlit.Value[1] >= '0' && lit.Value[1] <= '7' &&\n\t\t\t\tlit.Value[2] >= '0' && lit.Value[2] <= '7' {\n\n\t\t\t\tv, err := strconv.ParseInt(lit.Value, 10, 64)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treport.Report(pass, arg, fmt.Sprintf(\"file mode '%s' evaluates to %#o; did you mean '0%s'?\", lit.Value, v, lit.Value),\n\t\t\t\t\treport.Fixes(edit.Fix(\"Fix octal literal\", edit.ReplaceWithString(arg, \"0\"+lit.Value))))\n\t\t\t}\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.CallExpr)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa9002/sa9002_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa9002\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa9002/testdata/go1.0/CheckNonOctalFileMode/CheckNonOctalFileMode.go",
    "content": "package pkg\n\nimport \"os\"\n\nfunc fn() {\n\tos.OpenFile(\"\", 0, 644) //@ diag(`file mode`)\n}\n\nfunc fn2() (string, int, os.FileMode) {\n\treturn \"\", 0, 0\n}\n\nfunc fn3() {\n\tos.OpenFile(fn2())\n}\n"
  },
  {
    "path": "staticcheck/sa9002/testdata/go1.0/CheckNonOctalFileMode/CheckNonOctalFileMode.go.golden",
    "content": "package pkg\n\nimport \"os\"\n\nfunc fn() {\n\tos.OpenFile(\"\", 0, 0644) //@ diag(`file mode`)\n}\n\nfunc fn2() (string, int, os.FileMode) {\n\treturn \"\", 0, 0\n}\n\nfunc fn3() {\n\tos.OpenFile(fn2())\n}\n"
  },
  {
    "path": "staticcheck/sa9003/sa9003.go",
    "content": "package sa9003\n\nimport (\n\t\"go/ast\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA9003\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:      `Empty body in an if or else branch`,\n\t\tSince:      \"2017.1\",\n\t\tNonDefault: true,\n\t\tSeverity:   lint.SeverityWarning,\n\t\tMergeIf:    lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tif fn.Source() == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif irutil.IsExample(fn) {\n\t\t\tcontinue\n\t\t}\n\t\tcb := func(node ast.Node) bool {\n\t\t\tifstmt, ok := node.(*ast.IfStmt)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif ifstmt.Else != nil {\n\t\t\t\tb, ok := ifstmt.Else.(*ast.BlockStmt)\n\t\t\t\tif !ok || len(b.List) != 0 {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\treport.Report(pass, ifstmt.Else, \"empty branch\", report.FilterGenerated(), report.ShortRange())\n\t\t\t}\n\t\t\tif len(ifstmt.Body.List) != 0 {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treport.Report(pass, ifstmt, \"empty branch\", report.FilterGenerated(), report.ShortRange())\n\t\t\treturn true\n\t\t}\n\t\tif source := fn.Source(); source != nil {\n\t\t\tast.Inspect(source, cb)\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa9003/sa9003_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa9003\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa9003/testdata/go1.0/CheckEmptyBranch/CheckEmptyBranch.go",
    "content": "package pkg\n\nfunc fn1() {\n\tif true { //@ diag(`empty branch`)\n\t}\n\tif true { //@ diag(`empty branch`)\n\t} else { //@ diag(`empty branch`)\n\t}\n\tif true {\n\t\tprintln()\n\t}\n\n\tif true {\n\t\tprintln()\n\t} else { //@ diag(`empty branch`)\n\t}\n\n\tif true { //@ diag(`empty branch`)\n\t\t// TODO handle error\n\t}\n\n\tif true {\n\t} else {\n\t\tprintln()\n\t}\n\n\tif true {\n\t} else if false { //@ diag(`empty branch`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa9003/testdata/go1.0/CheckEmptyBranch/CheckEmptyBranch_generated.go",
    "content": "// Code generated by a human. DO NOT EDIT.\n\npackage pkg\n\nfunc fn2() {\n\tif true {\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa9003/testdata/go1.0/CheckEmptyBranch/CheckEmptyBranch_test.go",
    "content": "package pkg\n\nimport \"testing\"\n\nfunc TestFoo(t *testing.T) {\n\tif true { //@ diag(`empty branch`)\n\t\t// TODO\n\t}\n}\n\nfunc ExampleFoo() {\n\tif true {\n\t\t// TODO\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa9004/sa9004.go",
    "content": "package sa9004\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA9004\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Only the first constant has an explicit type`,\n\n\t\tText: `In a constant declaration such as the following:\n\n    const (\n        First byte = 1\n        Second     = 2\n    )\n\nthe constant Second does not have the same type as the constant First.\nThis construct shouldn't be confused with\n\n    const (\n        First byte = iota\n        Second\n    )\n\nwhere \\'First\\' and \\'Second\\' do indeed have the same type. The type is only\npassed on when no explicit value is assigned to the constant.\n\nWhen declaring enumerations with explicit values it is therefore\nimportant not to write\n\n    const (\n          EnumFirst EnumType = 1\n          EnumSecond         = 2\n          EnumThird          = 3\n    )\n\nThis discrepancy in types can cause various confusing behaviors and\nbugs.\n\n\nWrong type in variable declarations\n\nThe most obvious issue with such incorrect enumerations expresses\nitself as a compile error:\n\n    package pkg\n\n    const (\n        EnumFirst  uint8 = 1\n        EnumSecond       = 2\n    )\n\n    func fn(useFirst bool) {\n        x := EnumSecond\n        if useFirst {\n            x = EnumFirst\n        }\n    }\n\nfails to compile with\n\n    ./const.go:11:5: cannot use EnumFirst (type uint8) as type int in assignment\n\n\nLosing method sets\n\nA more subtle issue occurs with types that have methods and optional\ninterfaces. Consider the following:\n\n    package main\n\n    import \"fmt\"\n\n    type Enum int\n\n    func (e Enum) String() string {\n        return \"an enum\"\n    }\n\n    const (\n        EnumFirst  Enum = 1\n        EnumSecond      = 2\n    )\n\n    func main() {\n        fmt.Println(EnumFirst)\n        fmt.Println(EnumSecond)\n    }\n\nThis code will output\n\n    an enum\n    2\n\nas \\'EnumSecond\\' has no explicit type, and thus defaults to \\'int\\'.`,\n\t\tSince:    \"2019.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tdecl := node.(*ast.GenDecl)\n\t\tif !decl.Lparen.IsValid() {\n\t\t\treturn\n\t\t}\n\t\tif decl.Tok != token.CONST {\n\t\t\treturn\n\t\t}\n\n\t\tgroups := astutil.GroupSpecs(pass.Fset, decl.Specs)\n\tgroupLoop:\n\t\tfor _, group := range groups {\n\t\t\tif len(group) < 2 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif group[0].(*ast.ValueSpec).Type == nil {\n\t\t\t\t// first constant doesn't have a type\n\t\t\t\tcontinue groupLoop\n\t\t\t}\n\n\t\t\tfirstType := pass.TypesInfo.TypeOf(group[0].(*ast.ValueSpec).Values[0])\n\t\t\tfor i, spec := range group {\n\t\t\t\tspec := spec.(*ast.ValueSpec)\n\t\t\t\tif i > 0 && spec.Type != nil {\n\t\t\t\t\tcontinue groupLoop\n\t\t\t\t}\n\t\t\t\tif len(spec.Names) != 1 || len(spec.Values) != 1 {\n\t\t\t\t\tcontinue groupLoop\n\t\t\t\t}\n\n\t\t\t\tif !types.ConvertibleTo(pass.TypesInfo.TypeOf(spec.Values[0]), firstType) {\n\t\t\t\t\tcontinue groupLoop\n\t\t\t\t}\n\n\t\t\t\tswitch v := spec.Values[0].(type) {\n\t\t\t\tcase *ast.BasicLit:\n\t\t\t\tcase *ast.UnaryExpr:\n\t\t\t\t\tif _, ok := v.X.(*ast.BasicLit); !ok {\n\t\t\t\t\t\tcontinue groupLoop\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\t// if it's not a literal it might be typed, such as\n\t\t\t\t\t// time.Microsecond = 1000 * Nanosecond\n\t\t\t\t\tcontinue groupLoop\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar edits []analysis.TextEdit\n\t\t\ttyp := group[0].(*ast.ValueSpec).Type\n\t\t\tfor _, spec := range group[1:] {\n\t\t\t\tnspec := *spec.(*ast.ValueSpec)\n\t\t\t\tnspec.Type = typ\n\t\t\t\t// The position of `spec` node excludes comments (if any).\n\t\t\t\t// However, on generating the source back from the node, the comments are included. Setting `Comment` to nil ensures deduplication of comments.\n\t\t\t\tnspec.Comment = nil\n\t\t\t\tedits = append(edits, edit.ReplaceWithNode(pass.Fset, spec, &nspec))\n\t\t\t}\n\t\t\treport.Report(pass, group[0],\n\t\t\t\t\"only the first constant in this group has an explicit type\",\n\t\t\t\treport.Fixes(edit.Fix(\"Add type to all constants in group\", edits...)))\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.GenDecl)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa9004/sa9004_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa9004\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa9004/testdata/go1.0/CheckMissingEnumTypesInDeclaration/CheckMissingEnumTypesInDeclaration.go",
    "content": "package pkg\n\nconst c1 int = 0\nconst c2 = 0\n\nconst (\n\tc3 int = iota\n\tc4\n\tc5\n)\n\nconst (\n\tc6 int = 1 //@ diag(`only the first constant in this group has an explicit type`)\n\tc7     = 2 // comment for testing https://github.com/dominikh/go-tools/issues/866\n\tc8     = 3\n)\n\nconst (\n\tc9  int = 1\n\tc10     = 2\n\tc11     = 3\n\tc12 int = 4\n)\n\nconst (\n\tc13     = 1\n\tc14 int = 2\n\tc15 int = 3\n\tc16 int = 4\n)\n\nconst (\n\tc17     = 1\n\tc18 int = 2\n\tc19     = 3\n\tc20 int = 4\n)\n\nconst (\n\tc21 int = 1\n\n\tc22 = 2\n)\n\nconst (\n\tc23 int = 1\n\tc24 int = 2\n\n\tc25 string = \"\" //@ diag(`only the first constant in this group has an explicit type`)\n\tc26        = \"\"\n\n\tc27     = 1\n\tc28 int = 2\n\n\tc29 int = 1\n\tc30     = 2\n\tc31 int = 2\n\n\tc32 string = \"\" //@ diag(`only the first constant in this group has an explicit type`)\n\tc33        = \"\"\n)\n\nconst (\n\tc34 int = 1 //@ diag(`only the first constant in this group has an explicit type`)\n\tc35     = 2\n\n\tc36 int = 2\n)\n\nconst (\n\tc37 int = 1\n\tc38     = \"2\"\n)\n\nconst (\n\tc39 int8 = 1.0 //@ diag(`only the first constant in this group has an explicit type`)\n\tc40      = 'a'\n\tc41      = 3\n)\n\ntype String string\n\nconst (\n\tc42 String = \"\" //@ diag(`only the first constant in this group has an explicit type`)\n\tc43        = \"\"\n)\n"
  },
  {
    "path": "staticcheck/sa9004/testdata/go1.0/CheckMissingEnumTypesInDeclaration/CheckMissingEnumTypesInDeclaration.go.golden",
    "content": "package pkg\n\nconst c1 int = 0\nconst c2 = 0\n\nconst (\n\tc3 int = iota\n\tc4\n\tc5\n)\n\nconst (\n\tc6 int = 1 //@ diag(`only the first constant in this group has an explicit type`)\n\tc7 int = 2 // comment for testing https://github.com/dominikh/go-tools/issues/866\n\tc8 int = 3\n)\n\nconst (\n\tc9  int = 1\n\tc10     = 2\n\tc11     = 3\n\tc12 int = 4\n)\n\nconst (\n\tc13     = 1\n\tc14 int = 2\n\tc15 int = 3\n\tc16 int = 4\n)\n\nconst (\n\tc17     = 1\n\tc18 int = 2\n\tc19     = 3\n\tc20 int = 4\n)\n\nconst (\n\tc21 int = 1\n\n\tc22 = 2\n)\n\nconst (\n\tc23 int = 1\n\tc24 int = 2\n\n\tc25 string = \"\" //@ diag(`only the first constant in this group has an explicit type`)\n\tc26 string = \"\"\n\n\tc27     = 1\n\tc28 int = 2\n\n\tc29 int = 1\n\tc30     = 2\n\tc31 int = 2\n\n\tc32 string = \"\" //@ diag(`only the first constant in this group has an explicit type`)\n\tc33 string = \"\"\n)\n\nconst (\n\tc34 int = 1 //@ diag(`only the first constant in this group has an explicit type`)\n\tc35 int = 2\n\n\tc36 int = 2\n)\n\nconst (\n\tc37 int = 1\n\tc38     = \"2\"\n)\n\nconst (\n\tc39 int8 = 1.0 //@ diag(`only the first constant in this group has an explicit type`)\n\tc40 int8 = 'a'\n\tc41 int8 = 3\n)\n\ntype String string\n\nconst (\n\tc42 String = \"\" //@ diag(`only the first constant in this group has an explicit type`)\n\tc43 String = \"\"\n)\n"
  },
  {
    "path": "staticcheck/sa9005/sa9005.go",
    "content": "package sa9005\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/callcheck\"\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/knowledge\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName: \"SA9005\",\n\t\tRequires: []*analysis.Analyzer{\n\t\t\tbuildir.Analyzer,\n\t\t\t// Filtering generated code because it may include empty structs generated from data models.\n\t\t\tgenerated.Analyzer,\n\t\t},\n\t\tRun: callcheck.Analyzer(rules),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Trying to marshal a struct with no public fields nor custom marshaling`,\n\t\tText: `\nThe \\'encoding/json\\' and \\'encoding/xml\\' packages only operate on exported\nfields in structs, not unexported ones. It is usually an error to try\nto (un)marshal structs that only consist of unexported fields.\n\nThis check will not flag calls involving types that define custom\nmarshaling behavior, e.g. via \\'MarshalJSON\\' methods. It will also not\nflag empty structs.`,\n\t\tSince:    \"2019.2\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar rules = map[string]callcheck.Check{\n\t// TODO(dh): should we really flag XML? Even an empty struct\n\t// produces a non-zero amount of data, namely its type name.\n\t// Let's see if we encounter any false positives.\n\t//\n\t// Also, should we flag gob?\n\t\"encoding/json.Marshal\":           check(knowledge.Arg(\"json.Marshal.v\"), \"MarshalJSON\", \"MarshalText\"),\n\t\"encoding/xml.Marshal\":            check(knowledge.Arg(\"xml.Marshal.v\"), \"MarshalXML\", \"MarshalText\"),\n\t\"(*encoding/json.Encoder).Encode\": check(knowledge.Arg(\"(*encoding/json.Encoder).Encode.v\"), \"MarshalJSON\", \"MarshalText\"),\n\t\"(*encoding/xml.Encoder).Encode\":  check(knowledge.Arg(\"(*encoding/xml.Encoder).Encode.v\"), \"MarshalXML\", \"MarshalText\"),\n\n\t\"encoding/json.Unmarshal\":         check(knowledge.Arg(\"json.Unmarshal.v\"), \"UnmarshalJSON\", \"UnmarshalText\"),\n\t\"encoding/xml.Unmarshal\":          check(knowledge.Arg(\"xml.Unmarshal.v\"), \"UnmarshalXML\", \"UnmarshalText\"),\n\t\"(*encoding/json.Decoder).Decode\": check(knowledge.Arg(\"(*encoding/json.Decoder).Decode.v\"), \"UnmarshalJSON\", \"UnmarshalText\"),\n\t\"(*encoding/xml.Decoder).Decode\":  check(knowledge.Arg(\"(*encoding/xml.Decoder).Decode.v\"), \"UnmarshalXML\", \"UnmarshalText\"),\n}\n\nfunc check(argN int, meths ...string) callcheck.Check {\n\treturn func(call *callcheck.Call) {\n\t\tif code.IsGenerated(call.Pass, call.Instr.Pos()) {\n\t\t\treturn\n\t\t}\n\t\targ := call.Args[argN]\n\t\tT := arg.Value.Value.Type()\n\t\tTs, ok := typeutil.Dereference(T).Underlying().(*types.Struct)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tif Ts.NumFields() == 0 {\n\t\t\treturn\n\t\t}\n\t\tfields := typeutil.FlattenFields(Ts)\n\t\tfor _, field := range fields {\n\t\t\tif field.Var.Exported() {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t// OPT(dh): we could use a method set cache here\n\t\tms := call.Instr.Parent().Prog.MethodSets.MethodSet(T)\n\t\t// TODO(dh): we're not checking the signature, which can cause false negatives.\n\t\t// This isn't a huge problem, however, since vet complains about incorrect signatures.\n\t\tfor _, meth := range meths {\n\t\t\tif ms.Lookup(nil, meth) != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\targ.Invalid(fmt.Sprintf(\"struct type '%s' doesn't have any exported fields, nor custom marshaling\", typeutil.Dereference(T)))\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa9005/sa9005_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa9005\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa9005/testdata/go1.0/CheckNoopMarshal/CheckNoopMarshal.go",
    "content": "package pkg\n\nimport (\n\t\"encoding/json\"\n\t\"encoding/xml\"\n)\n\ntype T1 struct{}\ntype T2 struct{ x int }\ntype T3 struct{ X int }\ntype T4 struct{ T3 }\ntype t5 struct{ X int }\ntype T6 struct{ t5 }\ntype T7 struct{ x int }\n\nfunc (T7) MarshalJSON() ([]byte, error) { return nil, nil }\nfunc (*T7) UnmarshalJSON([]byte) error  { return nil }\n\ntype T8 struct{ x int }\n\nfunc (T8) MarshalXML() ([]byte, error)                         { return nil, nil }\nfunc (*T8) UnmarshalXML(*xml.Decoder, *xml.StartElement) error { return nil }\n\ntype T9 struct{}\n\nfunc (T9) MarshalText() ([]byte, error) { return nil, nil }\nfunc (*T9) UnmarshalText([]byte) error  { return nil }\n\ntype T10 struct{}\ntype T11 struct{ T10 }\ntype T12 struct{ T7 }\ntype t13 struct{}\n\nfunc (t13) MarshalJSON() ([]byte, error) { return nil, nil }\n\ntype T14 struct{ t13 }\ntype T15 struct{ *t13 }\ntype T16 struct{ *T3 }\ntype T17 struct{ *T17 }\ntype T18 struct {\n\tT17\n\tActual int\n}\n\ntype t19 string\ntype T20 string\n\ntype T21 struct{ t19 }\ntype T22 struct{ T20 }\ntype T23 struct{ *T20 }\n\nfunc fn() {\n\t// don't flag structs with no fields\n\tjson.Marshal(T1{})\n\t// no exported fields\n\tjson.Marshal(T2{}) //@ diag(`struct type 'example.com/CheckNoopMarshal.T2' doesn't have any exported fields, nor custom marshaling`)\n\t// pointer vs non-pointer makes no difference\n\tjson.Marshal(&T2{}) //@ diag(`struct type 'example.com/CheckNoopMarshal.T2' doesn't have any exported fields, nor custom marshaling`)\n\t// exported field\n\tjson.Marshal(T3{})\n\t// exported field, pointer makes no difference\n\tjson.Marshal(&T3{})\n\t// embeds struct with exported fields\n\tjson.Marshal(T4{})\n\t// exported field\n\tjson.Marshal(t5{})\n\t// embeds unexported type, but said type does have exported fields\n\tjson.Marshal(T6{})\n\t// MarshalJSON\n\tjson.Marshal(T7{})\n\t// MarshalXML does not apply to JSON\n\tjson.Marshal(T8{}) //@ diag(`struct type 'example.com/CheckNoopMarshal.T8' doesn't have any exported fields, nor custom marshaling`)\n\t// MarshalText\n\tjson.Marshal(T9{})\n\t// embeds exported struct, but it has no fields\n\tjson.Marshal(T11{}) //@ diag(`struct type 'example.com/CheckNoopMarshal.T11' doesn't have any exported fields, nor custom marshaling`)\n\t// embeds type with MarshalJSON\n\tjson.Marshal(T12{})\n\t// embeds type with MarshalJSON and type isn't exported\n\tjson.Marshal(T14{})\n\t// embedded pointer with MarshalJSON\n\tjson.Marshal(T15{})\n\t// embedded pointer to struct with exported fields\n\tjson.Marshal(T16{})\n\t// don't recurse forever on recursive data structure\n\tjson.Marshal(T17{}) //@ diag(`struct type 'example.com/CheckNoopMarshal.T17' doesn't have any exported fields, nor custom marshaling`)\n\tjson.Marshal(T18{})\n\n\t// embedding an unexported, non-struct type\n\tjson.Marshal(T21{}) //@ diag(`struct type 'example.com/CheckNoopMarshal.T21' doesn't have any exported fields, nor custom marshaling`)\n\t// embedding an exported, non-struct type\n\tjson.Marshal(T22{})\n\t// embedding an exported, non-struct type\n\tjson.Marshal(T23{})\n\n\t// MarshalJSON does not apply to XML\n\txml.Marshal(T7{}) //@ diag(`struct type 'example.com/CheckNoopMarshal.T7' doesn't have any exported fields, nor custom marshaling`)\n\t// MarshalXML\n\txml.Marshal(T8{})\n\n\tvar t2 T2\n\tvar t3 T3\n\tvar t7 T7\n\tvar t8 T8\n\tvar t9 T9\n\t// check that all other variations of methods also work\n\tjson.Unmarshal(nil, &t2) //@ diag(`struct type 'example.com/CheckNoopMarshal.T2' doesn't have any exported fields, nor custom marshaling`)\n\tjson.Unmarshal(nil, &t3)\n\tjson.Unmarshal(nil, &t9)\n\txml.Unmarshal(nil, &t2) //@ diag(`struct type 'example.com/CheckNoopMarshal.T2' doesn't have any exported fields, nor custom marshaling`)\n\txml.Unmarshal(nil, &t3)\n\txml.Unmarshal(nil, &t9)\n\t(*json.Decoder)(nil).Decode(&t2) //@ diag(`struct type 'example.com/CheckNoopMarshal.T2' doesn't have any exported fields, nor custom marshaling`)\n\t(*json.Decoder)(nil).Decode(&t3)\n\t(*json.Decoder)(nil).Decode(&t9)\n\t(*json.Encoder)(nil).Encode(t2) //@ diag(`struct type 'example.com/CheckNoopMarshal.T2' doesn't have any exported fields, nor custom marshaling`)\n\t(*json.Encoder)(nil).Encode(t3)\n\t(*json.Encoder)(nil).Encode(t9)\n\t(*xml.Decoder)(nil).Decode(&t2) //@ diag(`struct type 'example.com/CheckNoopMarshal.T2' doesn't have any exported fields, nor custom marshaling`)\n\t(*xml.Decoder)(nil).Decode(&t3)\n\t(*xml.Decoder)(nil).Decode(&t9)\n\t(*xml.Encoder)(nil).Encode(t2) //@ diag(`struct type 'example.com/CheckNoopMarshal.T2' doesn't have any exported fields, nor custom marshaling`)\n\t(*xml.Encoder)(nil).Encode(t3)\n\t(*xml.Encoder)(nil).Encode(t9)\n\n\t(*json.Decoder)(nil).Decode(&t7)\n\t(*json.Decoder)(nil).Decode(&t8) //@ diag(`struct type 'example.com/CheckNoopMarshal.T8' doesn't have any exported fields, nor custom marshaling`)\n\t(*json.Encoder)(nil).Encode(t7)\n\t(*json.Encoder)(nil).Encode(t8) //@ diag(`struct type 'example.com/CheckNoopMarshal.T8' doesn't have any exported fields, nor custom marshaling`)\n\t(*xml.Decoder)(nil).Decode(&t7) //@ diag(`struct type 'example.com/CheckNoopMarshal.T7' doesn't have any exported fields, nor custom marshaling`)\n\t(*xml.Decoder)(nil).Decode(&t8)\n\t(*xml.Encoder)(nil).Encode(t7) //@ diag(`struct type 'example.com/CheckNoopMarshal.T7' doesn't have any exported fields, nor custom marshaling`)\n\t(*xml.Encoder)(nil).Encode(t8)\n\n}\n\nvar _, _ = json.Marshal(T9{})\n"
  },
  {
    "path": "staticcheck/sa9006/sa9006.go",
    "content": "package sa9006\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA9006\",\n\t\tRun:      run,\n\t\tRequires: code.RequiredAnalyzers,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Dubious bit shifting of a fixed size integer value`,\n\t\tText: `Bit shifting a value past its size will always clear the value.\n\nFor instance:\n\n    v := int8(42)\n    v >>= 8\n\nwill always result in 0.\n\nThis check flags bit shifting operations on fixed size integer values only.\nThat is, int, uint and uintptr are never flagged to avoid potential false\npositives in somewhat exotic but valid bit twiddling tricks:\n\n    // Clear any value above 32 bits if integers are more than 32 bits.\n    func f(i int) int {\n        v := i >> 32\n        v = v << 32\n        return i-v\n    }`,\n\t\tSince:    \"2020.2\",\n\t\tSeverity: lint.SeverityWarning,\n\t\t// Technically this should be MergeIfAll, because the type of\n\t\t// v might be different for different build tags. Practically,\n\t\t// don't write code that depends on that.\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\nvar (\n\tcheckFixedLengthTypeShiftQ = pattern.MustParse(`\n\t\t(Or\n\t\t\t(AssignStmt _ (Or \">>=\" \"<<=\") _)\n\t\t\t(BinaryExpr _ (Or \">>\" \"<<\") _))\n\t`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tisDubiousShift := func(x, y ast.Expr) (int64, int64, bool) {\n\t\ttyp, ok := pass.TypesInfo.TypeOf(x).Underlying().(*types.Basic)\n\t\tif !ok {\n\t\t\treturn 0, 0, false\n\t\t}\n\t\tswitch typ.Kind() {\n\t\tcase types.Int8, types.Int16, types.Int32, types.Int64,\n\t\t\ttypes.Uint8, types.Uint16, types.Uint32, types.Uint64:\n\t\t\t// We're only interested in fixed–size types.\n\t\tdefault:\n\t\t\treturn 0, 0, false\n\t\t}\n\n\t\tconst bitsInByte = 8\n\t\ttypeBits := pass.TypesSizes.Sizeof(typ) * bitsInByte\n\n\t\tshiftLength, ok := code.ExprToInt(pass, y)\n\t\tif !ok {\n\t\t\treturn 0, 0, false\n\t\t}\n\n\t\treturn typeBits, shiftLength, shiftLength >= typeBits\n\t}\n\n\tfor node := range code.Matches(pass, checkFixedLengthTypeShiftQ) {\n\t\tswitch e := node.(type) {\n\t\tcase *ast.AssignStmt:\n\t\t\tif size, shift, yes := isDubiousShift(e.Lhs[0], e.Rhs[0]); yes {\n\t\t\t\treport.Report(pass, e, fmt.Sprintf(\"shifting %d-bit value by %d bits will always clear it\", size, shift))\n\t\t\t}\n\t\tcase *ast.BinaryExpr:\n\t\t\tif size, shift, yes := isDubiousShift(e.X, e.Y); yes {\n\t\t\t\treport.Report(pass, e, fmt.Sprintf(\"shifting %d-bit value by %d bits will always clear it\", size, shift))\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa9006/sa9006_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa9006\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa9006/testdata/go1.0/CheckStaticBitShift/CheckStaticBitShift.go",
    "content": "package pkg\n\n// Partially copied from go vet's test suite.\n\n// Copyright 2014 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE-THIRD-PARTY file.\n\ntype Number int8\n\nfunc fn() {\n\tvar n8 Number\n\tn8 <<= 8 //@ diag(`will always clear it`)\n\n\tvar i8 int8\n\t_ = i8 << 7\n\t_ = (i8 + 1) << 8 //@ diag(`will always clear it`)\n\t_ = i8 << (7 + 1) //@ diag(`will always clear it`)\n\t_ = i8 >> 8       //@ diag(`will always clear it`)\n\ti8 <<= 8          //@ diag(`will always clear it`)\n\ti8 >>= 8          //@ diag(`will always clear it`)\n\ti8 <<= 12         //@ diag(`will always clear it`)\n\n\tvar i16 int16\n\t_ = i16 << 15\n\t_ = i16 << 16 //@ diag(`will always clear it`)\n\t_ = i16 >> 16 //@ diag(`will always clear it`)\n\ti16 <<= 16    //@ diag(`will always clear it`)\n\ti16 >>= 16    //@ diag(`will always clear it`)\n\ti16 <<= 18    //@ diag(`will always clear it`)\n\n\tvar i32 int32\n\t_ = i32 << 31\n\t_ = i32 << 32 //@ diag(`will always clear it`)\n\t_ = i32 >> 32 //@ diag(`will always clear it`)\n\ti32 <<= 32    //@ diag(`will always clear it`)\n\ti32 >>= 32    //@ diag(`will always clear it`)\n\ti32 <<= 40    //@ diag(`will always clear it`)\n\n\tvar i64 int64\n\t_ = i64 << 63\n\t_ = i64 << 64 //@ diag(`will always clear it`)\n\t_ = i64 >> 64 //@ diag(`will always clear it`)\n\ti64 <<= 64    //@ diag(`will always clear it`)\n\ti64 >>= 64    //@ diag(`will always clear it`)\n\ti64 <<= 70    //@ diag(`will always clear it`)\n\n\tvar u8 uint8\n\t_ = u8 << 7\n\t_ = u8 << 8 //@ diag(`will always clear it`)\n\t_ = u8 >> 8 //@ diag(`will always clear it`)\n\tu8 <<= 8    //@ diag(`will always clear it`)\n\tu8 >>= 8    //@ diag(`will always clear it`)\n\tu8 <<= 12   //@ diag(`will always clear it`)\n\n\tvar u16 uint16\n\t_ = u16 << 15\n\t_ = u16 << 16 //@ diag(`will always clear it`)\n\t_ = u16 >> 16 //@ diag(`will always clear it`)\n\tu16 <<= 16    //@ diag(`will always clear it`)\n\tu16 >>= 16    //@ diag(`will always clear it`)\n\tu16 <<= 18    //@ diag(`will always clear it`)\n\n\tvar u32 uint32\n\t_ = u32 << 31\n\t_ = u32 << 32 //@ diag(`will always clear it`)\n\t_ = u32 >> 32 //@ diag(`will always clear it`)\n\tu32 <<= 32    //@ diag(`will always clear it`)\n\tu32 >>= 32    //@ diag(`will always clear it`)\n\tu32 <<= 40    //@ diag(`will always clear it`)\n\n\tvar u64 uint64\n\t_ = u64 << 63\n\t_ = u64 << 64 //@ diag(`will always clear it`)\n\t_ = u64 >> 64 //@ diag(`will always clear it`)\n\tu64 <<= 64    //@ diag(`will always clear it`)\n\tu64 >>= 64    //@ diag(`will always clear it`)\n\tu64 <<= 70    //@ diag(`will always clear it`)\n\t_ = u64 << u64\n}\n\nfunc fn1() {\n\tvar ui uint\n\t_ = ui << 64\n\t_ = ui >> 64\n\tui <<= 64\n\tui >>= 64\n\n\tvar uptr uintptr\n\t_ = uptr << 64\n\t_ = uptr >> 64\n\tuptr <<= 64\n\tuptr >>= 64\n\n\tvar i int\n\t_ = i << 64\n\t_ = i >> 64\n\ti <<= 64\n\ti >>= 64\n}\n"
  },
  {
    "path": "staticcheck/sa9007/sa9007.go",
    "content": "package sa9007\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA9007\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Deleting a directory that shouldn't be deleted\",\n\t\tText: `\nIt is virtually never correct to delete system directories such as\n/tmp or the user's home directory. However, it can be fairly easy to\ndo by mistake, for example by mistakenly using \\'os.TempDir\\' instead\nof \\'ioutil.TempDir\\', or by forgetting to add a suffix to the result\nof \\'os.UserHomeDir\\'.\n\nWriting\n\n    d := os.TempDir()\n    defer os.RemoveAll(d)\n\nin your unit tests will have a devastating effect on the stability of your system.\n\nThis check flags attempts at deleting the following directories:\n\n- os.TempDir\n- os.UserCacheDir\n- os.UserConfigDir\n- os.UserHomeDir\n`,\n\t\tSince:    \"2022.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tfor _, b := range fn.Blocks {\n\t\t\tfor _, instr := range b.Instrs {\n\t\t\t\tcall, ok := instr.(ir.CallInstruction)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !irutil.IsCallTo(call.Common(), \"os.RemoveAll\") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tkind := \"\"\n\t\t\t\tex := \"\"\n\t\t\t\tcallName := \"\"\n\t\t\t\targ := irutil.Flatten(call.Common().Args[0])\n\t\t\t\tswitch arg := arg.(type) {\n\t\t\t\tcase *ir.Call:\n\t\t\t\t\tcallName = irutil.CallName(&arg.Call)\n\t\t\t\t\tif callName != \"os.TempDir\" {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tkind = \"temporary\"\n\t\t\t\t\tex = os.TempDir()\n\t\t\t\tcase *ir.Extract:\n\t\t\t\t\tif arg.Index != 0 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tfirst, ok := arg.Tuple.(*ir.Call)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tcallName = irutil.CallName(&first.Call)\n\t\t\t\t\tswitch callName {\n\t\t\t\t\tcase \"os.UserCacheDir\":\n\t\t\t\t\t\tkind = \"cache\"\n\t\t\t\t\t\tex, _ = os.UserCacheDir()\n\t\t\t\t\tcase \"os.UserConfigDir\":\n\t\t\t\t\t\tkind = \"config\"\n\t\t\t\t\t\tex, _ = os.UserConfigDir()\n\t\t\t\t\tcase \"os.UserHomeDir\":\n\t\t\t\t\t\tkind = \"home\"\n\t\t\t\t\t\tex, _ = os.UserHomeDir()\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif ex == \"\" {\n\t\t\t\t\treport.Report(pass, call, fmt.Sprintf(\"this call to os.RemoveAll deletes the user's entire %s directory, not a subdirectory therein\", kind),\n\t\t\t\t\t\treport.Related(arg, fmt.Sprintf(\"this call to %s returns the user's %s directory\", callName, kind)))\n\t\t\t\t} else {\n\t\t\t\t\treport.Report(pass, call, fmt.Sprintf(\"this call to os.RemoveAll deletes the user's entire %s directory, not a subdirectory therein\", kind),\n\t\t\t\t\t\treport.Related(arg, fmt.Sprintf(\"this call to %s returns the user's %s directory, for example %s\", callName, kind, ex)))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa9007/sa9007_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa9007\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa9007/testdata/go1.0/CheckBadRemoveAll/CheckBadRemoveAll.go",
    "content": "package pkg\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n)\n\nfunc fn1() {\n\tx := os.TempDir()\n\tdefer os.RemoveAll(x) //@ diag(`deletes the user's entire temporary directory`)\n\n\tx = \"\"\n}\n\nfunc fn2() {\n\tx := os.TempDir()\n\tif true {\n\t\tos.RemoveAll(x) //@ diag(`deletes the user's entire temporary directory`)\n\t}\n}\n\nfunc fn3() {\n\tx := os.TempDir()\n\tif true {\n\t\tx = filepath.Join(x, \"foo\")\n\t}\n\t// we don't flag this to avoid false positives in infeasible paths\n\tos.RemoveAll(x)\n}\n\nfunc fn4() {\n\tx, _ := os.UserCacheDir()\n\tos.RemoveAll(x) //@ diag(`deletes the user's entire cache directory`)\n\n\tx, _ = os.UserConfigDir()\n\tos.RemoveAll(x) //@ diag(`deletes the user's entire config directory`)\n\n\tx, _ = os.UserHomeDir()\n\tos.RemoveAll(x) //@ diag(`deletes the user's entire home directory`)\n}\n"
  },
  {
    "path": "staticcheck/sa9008/sa9008.go",
    "content": "package sa9008\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA9008\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{buildir.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `\\'else\\' branch of a type assertion is probably not reading the right value`,\n\t\tText: `\nWhen declaring variables as part of an \\'if\\' statement (like in \\\"if\nfoo := ...; foo {\\\"), the same variables will also be in the scope of\nthe \\'else\\' branch. This means that in the following example\n\n    if x, ok := x.(int); ok {\n        // ...\n    } else {\n        fmt.Printf(\"unexpected type %T\", x)\n    }\n\n\\'x\\' in the \\'else\\' branch will refer to the \\'x\\' from \\'x, ok\n:=\\'; it will not refer to the \\'x\\' that is being type-asserted. The\nresult of a failed type assertion is the zero value of the type that\nis being asserted to, so \\'x\\' in the else branch will always have the\nvalue \\'0\\' and the type \\'int\\'.\n`,\n\t\tSince:    \"2022.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar typeAssertionShadowingElseQ = pattern.MustParse(`(IfStmt (AssignStmt [obj@(Ident _) ok@(Ident _)] \":=\" assert@(TypeAssertExpr obj _)) ok _ elseBranch)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// TODO(dh): without the IR-based verification, this check is able\n\t// to find more bugs, but also more prone to false positives. It\n\t// would be a good candidate for the 'codereview' category of\n\t// checks.\n\n\tirpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg\n\tfor _, m := range code.Matches(pass, typeAssertionShadowingElseQ) {\n\t\tshadow := pass.TypesInfo.ObjectOf(m.State[\"obj\"].(*ast.Ident))\n\t\tshadowed := m.State[\"assert\"].(*ast.TypeAssertExpr).X\n\n\t\tpath, exact := astutil.PathEnclosingInterval(code.File(pass, shadow), shadow.Pos(), shadow.Pos())\n\t\tif !exact {\n\t\t\t// TODO(dh): when can this happen?\n\t\t\tcontinue\n\t\t}\n\t\tirfn := ir.EnclosingFunction(irpkg, path)\n\t\tif irfn == nil {\n\t\t\t// For example for functions named \"_\", because we don't generate IR for them.\n\t\t\tcontinue\n\t\t}\n\n\t\tshadoweeIR, isAddr := irfn.ValueForExpr(m.State[\"obj\"].(*ast.Ident))\n\t\tif shadoweeIR == nil || isAddr {\n\t\t\t// TODO(dh): is this possible?\n\t\t\tcontinue\n\t\t}\n\n\t\tvar branch ast.Node\n\t\tswitch br := m.State[\"elseBranch\"].(type) {\n\t\tcase ast.Node:\n\t\t\tbranch = br\n\t\tcase []ast.Stmt:\n\t\t\tbranch = &ast.BlockStmt{List: br}\n\t\tcase nil:\n\t\t\tcontinue\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"unexpected type %T\", br))\n\t\t}\n\n\t\tast.Inspect(branch, func(node ast.Node) bool {\n\t\t\tident, ok := node.(*ast.Ident)\n\t\t\tif !ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif pass.TypesInfo.ObjectOf(ident) != shadow {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tv, isAddr := irfn.ValueForExpr(ident)\n\t\t\tif v == nil || isAddr {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif irutil.Flatten(v) != shadoweeIR {\n\t\t\t\t// Same types.Object, but different IR value. This\n\t\t\t\t// either means that the variable has been\n\t\t\t\t// assigned to since the type assertion, or that\n\t\t\t\t// the variable has escaped to the heap. Either\n\t\t\t\t// way, we shouldn't flag reads of it.\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\treport.Report(pass, ident,\n\t\t\t\tfmt.Sprintf(\"%s refers to the result of a failed type assertion and is a zero value, not the value that was being type-asserted\", report.Render(pass, ident)),\n\t\t\t\treport.Related(shadow, \"this is the variable being read\"),\n\t\t\t\treport.Related(shadowed, \"this is the variable being shadowed\"))\n\t\t\treturn true\n\t\t})\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa9008/sa9008_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa9008\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa9008/testdata/go1.0/CheckTypeAssertionShadowingElse/CheckTypeAssertionShadowingElse.go",
    "content": "package pkg\n\nfunc fn1(x interface{}) {\n\tif x, ok := x.(int); ok {\n\t\t_ = x\n\t} else {\n\t\t_ = x //@ diag(`x refers to the result of a failed type assertion`)\n\t\tx = 1\n\t\t// No diagnostic, x is no longer the zero value\n\t\t_ = x\n\t}\n\n\tif x, ok := x.(int); ok {\n\t} else {\n\t\t_ = x //@ diag(`x refers to the result of a failed type assertion`)\n\t\t_ = &x\n\t\t_ = x\n\t}\n\n\tif y, ok := x.(int); ok {\n\t\t_ = y\n\t} else {\n\t\t// No diagnostic because x isn't shadowed\n\t\t_ = x\n\t}\n\n\tif y, ok := x.(int); ok {\n\t\t_ = y\n\t} else {\n\t\t// No diagnostic because y isn't shadowing x\n\t\t_ = y\n\t}\n\n\tif x, ok := x.(int); ok && true {\n\t\t_ = x\n\t} else {\n\t\t// No diagnostic because the condition isn't simply 'ok'\n\t\t_ = x\n\t}\n\n\tif x, ok := x.(*int); ok {\n\t\t_ = x\n\t} else if x != nil { //@ diag(`x refers to`)\n\t} else if x == nil { //@ diag(`x refers to`)\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa9009/sa9009.go",
    "content": "package sa9009\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA9009\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Ineffectual Go compiler directive\",\n\t\tText: `\nA potential Go compiler directive was found, but is ineffectual as it begins\nwith whitespace.`,\n\t\tSince:    \"2024.1\",\n\t\tSeverity: lint.SeverityWarning,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, f := range pass.Files {\n\t\tfor _, cg := range f.Comments {\n\t\t\tfor _, c := range cg.List {\n\t\t\t\t// Compiler directives have to be // comments\n\t\t\t\tif !strings.HasPrefix(c.Text, \"//\") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif pass.Fset.PositionFor(c.Pos(), false).Column != 1 {\n\t\t\t\t\t// Compiler directives have to be top-level. This also\n\t\t\t\t\t// avoids a false positive for\n\t\t\t\t\t// 'import _ \"unsafe\" // go:linkname'\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\ttext := strings.TrimLeft(c.Text[2:], \" \\t\")\n\t\t\t\tif len(text) == len(c.Text[2:]) {\n\t\t\t\t\t// There was no leading whitespace\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !strings.HasPrefix(text, \"go:\") {\n\t\t\t\t\t// Not an attempted compiler directive\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\ttext = text[3:]\n\t\t\t\tif len(text) == 0 || text[0] < 'a' || text[0] > 'z' {\n\t\t\t\t\t// A letter other than a-z after \"go:\", so unlikely to be an\n\t\t\t\t\t// attempted compiler directive\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treport.Report(pass, c,\n\t\t\t\t\tfmt.Sprintf(\n\t\t\t\t\t\t\"ineffectual compiler directive due to extraneous space: %q\",\n\t\t\t\t\t\tc.Text))\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa9009/sa9009_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa9009\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa9009/testdata/go1.0/Directives/pkg.go",
    "content": "package pkg\n\nimport _ \"unsafe\" // go:linkname\n\n// go:linkname bad1 foo.Bad1 //@ diag(`ineffectual compiler directive`)\nfunc bad1() {\n}\n\n//\tgo:foobar sdlkfjlaksdj //@ diag(`ineffectual compiler directive`)\nfunc bad2() {\n}\n\n/* go:foobar 1234*/ // cannot be a compiler directive\nfunc bad3() {\n}\n\n/*\tgo:foobar 4321*/ // cannot be a compiler directive\nfunc bad4() {\n}\n\n //go:linkname good1 good.One\nfunc good1() {\n}\n\n\t//go:foobar blahblah\nfunc good2() {\n}\n\n /*go:foobar asdf*/ // cannot be a compiler directive\nfunc good3() {\n}\n\n\t/*go:foobar asdf*/ // cannot be a compiler directive\nfunc good4() {\n}\n\n\n// go: probably just talking about Go\nfunc good5() {\n}\n"
  },
  {
    "path": "staticcheck/sa9010/sa9010.go",
    "content": "package sa9010\n\nimport (\n\t\"go/ast\"\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"SA9010\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Returned function should be called in defer`,\n\t\tText: `\nIf you have a function such as:\n\n    func f() func() {\n        // Do something.\n        return func() {\n            // Do something.\n        }\n    }\n\nThen calling that in defer:\n\n    defer f()\n\nIs almost always a mistake, since you typically want to call the returned\nfunction:\n\n    defer f()()\n`,\n\t\tSince:    \"Unreleased\",\n\t\tSeverity: lint.SeverityWarning,\n\t\tMergeIf:  lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(n ast.Node) {\n\t\tdef := n.(*ast.DeferStmt)\n\t\tif _, ok := pass.TypesInfo.TypeOf(def.Call).Underlying().(*types.Signature); ok {\n\t\t\treport.Report(pass, def, \"deferred return function not called\")\n\t\t}\n\t}\n\n\tcode.Preorder(pass, fn, (*ast.DeferStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "staticcheck/sa9010/sa9010_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage sa9010\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "staticcheck/sa9010/testdata/go1.0/example.com/deferr/deferr.go",
    "content": "package deferr\n\nfunc x() {\n\tvar (\n\t\tt                t1\n\t\ttt               t2\n\t\tvarReturnNothing = func() {}\n\t\tvarReturnInt     = func() int { return 0 }\n\t\tvarReturnFunc    = func() func() { return func() {} }\n\t\tvarReturnFuncInt = func(int) func(int) int { return func(int) int { return 0 } }\n\t\tvarReturnMulti   = func() (int, func()) { return 0, func() {} }\n\n\t\tnamedReturnNothing = named(func() {})\n\t\tnamedReturnFunc    = namedReturn(func() func() { return func() {} })\n\t)\n\n\t// Correct.\n\tdefer returnNothing()\n\tdefer varReturnNothing()\n\tdefer namedReturnNothing()\n\tdefer t.returnNothing()\n\tdefer tt.returnNothing()\n\tdefer tt.t.returnNothing()\n\tdefer func() {}()\n\tdefer close(make(chan int))\n\n\tdefer returnInt()\n\tdefer varReturnInt()\n\tdefer t.returnInt()\n\tdefer tt.returnInt()\n\tdefer tt.t.returnInt()\n\tdefer func() int { return 0 }()\n\n\tdefer returnFunc()()\n\tdefer varReturnFunc()()\n\tdefer namedReturnFunc()()\n\tdefer t.returnFunc()()\n\tdefer tt.returnFunc()()\n\tdefer tt.t.returnFunc()()\n\tdefer func() func() { return func() {} }()()\n\n\tdefer returnFuncInt(0)(0)\n\tdefer varReturnFuncInt(0)(0)\n\tdefer t.returnFuncInt(0)(0)\n\tdefer tt.returnFuncInt(0)(0)\n\tdefer tt.t.returnFuncInt(0)(0)\n\tdefer func(int) func(int) int { return func(int) int { return 0 } }(0)(0)\n\n\tdefer returnMulti()\n\tdefer varReturnMulti()\n\tdefer t.returnMulti()\n\tdefer tt.returnMulti()\n\tdefer tt.t.returnMulti()\n\tdefer func() (int, func()) { return 0, func() {} }()\n\n\t// Wrong.\n\tdefer returnFunc()                                                     //@ diag(`deferred return function not called`)\n\tdefer varReturnFunc()                                                  //@ diag(`deferred return function not called`)\n\tdefer namedReturnFunc()                                                //@ diag(`deferred return function not called`)\n\tdefer t.returnFunc()                                                   //@ diag(`deferred return function not called`)\n\tdefer tt.returnFunc()                                                  //@ diag(`deferred return function not called`)\n\tdefer tt.t.returnFunc()                                                //@ diag(`deferred return function not called`)\n\tdefer func() func() { return func() {} }()                             //@ diag(`deferred return function not called`)\n\tdefer returnFuncInt(0)                                                 //@ diag(`deferred return function not called`)\n\tdefer t.returnFuncInt(0)                                               //@ diag(`deferred return function not called`)\n\tdefer tt.returnFuncInt(0)                                              //@ diag(`deferred return function not called`)\n\tdefer tt.t.returnFuncInt(0)                                            //@ diag(`deferred return function not called`)\n\tdefer func(int) func(int) int { return func(int) int { return 0 } }(0) //@ diag(`deferred return function not called`)\n\n\t// Function returns a function which returns another function. This is\n\t// getting silly, but we do flag it.\n\tdefer silly1()()             //@ diag(`deferred return function not called`)\n\tdefer func() func() func() { //@ diag(`deferred return function not called`)\n\t\treturn func() func() {\n\t\t\treturn func() {}\n\t\t}\n\t}()()\n}\n\nfunc returnNothing()                  {}\nfunc returnInt() int                  { return 0 }\nfunc returnFunc() func()              { return func() {} }\nfunc returnFuncInt(int) func(int) int { return func(int) int { return 0 } }\nfunc returnMulti() (int, func())      { return 0, func() {} }\n\ntype (\n\tt1          struct{}\n\tt2          struct{ t t1 }\n\tnamed       func()\n\tnamedReturn func() func()\n)\n\nfunc (t1) returnNothing()                  {}\nfunc (t1) returnInt() int                  { return 0 }\nfunc (t1) returnFunc() func()              { return func() {} }\nfunc (t1) returnFuncInt(int) func(int) int { return func(int) int { return 0 } }\nfunc (t1) returnMulti() (int, func())      { return 0, func() {} }\n\nfunc (*t2) returnNothing()                  {}\nfunc (*t2) returnInt() int                  { return 0 }\nfunc (*t2) returnFunc() func()              { return func() {} }\nfunc (*t2) returnFuncInt(int) func(int) int { return func(int) int { return 0 } }\nfunc (*t2) returnMulti() (int, func())      { return 0, func() {} }\n\nfunc silly1() func() func() {\n\treturn func() func() {\n\t\treturn func() {}\n\t}\n}\n"
  },
  {
    "path": "staticcheck/sa9010/testdata/go1.18/deferr/deferr.go",
    "content": "package deferr\n\nfunc x() {\n\n\tdefer tpReturnFuncInt[int](0) //@ diag(`deferred return function not called`)\n\tdefer tpReturnFuncInt(0)(0)\n}\n\nfunc tpReturnFuncInt[T any](T) func(int) int { return func(int) int { return 0 } }\n"
  },
  {
    "path": "staticcheck.conf",
    "content": "checks = [\"inherit\", \"-SA9003\"]\n"
  },
  {
    "path": "structlayout/layout.go",
    "content": "package structlayout\n\nimport \"fmt\"\n\ntype Field struct {\n\tName      string `json:\"name\"`\n\tType      string `json:\"type\"`\n\tStart     int64  `json:\"start\"`\n\tEnd       int64  `json:\"end\"`\n\tSize      int64  `json:\"size\"`\n\tAlign     int64  `json:\"align\"`\n\tIsPadding bool   `json:\"is_padding\"`\n}\n\nfunc (f Field) String() string {\n\tif f.IsPadding {\n\t\treturn fmt.Sprintf(\"%s: %d-%d (size %d, align %d)\",\n\t\t\t\"padding\", f.Start, f.End, f.Size, f.Align)\n\t}\n\treturn fmt.Sprintf(\"%s %s: %d-%d (size %d, align %d)\",\n\t\tf.Name, f.Type, f.Start, f.End, f.Size, f.Align)\n}\n"
  },
  {
    "path": "stylecheck/analysis.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage stylecheck\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/stylecheck/st1000\"\n\t\"honnef.co/go/tools/stylecheck/st1001\"\n\t\"honnef.co/go/tools/stylecheck/st1003\"\n\t\"honnef.co/go/tools/stylecheck/st1005\"\n\t\"honnef.co/go/tools/stylecheck/st1006\"\n\t\"honnef.co/go/tools/stylecheck/st1008\"\n\t\"honnef.co/go/tools/stylecheck/st1011\"\n\t\"honnef.co/go/tools/stylecheck/st1012\"\n\t\"honnef.co/go/tools/stylecheck/st1013\"\n\t\"honnef.co/go/tools/stylecheck/st1015\"\n\t\"honnef.co/go/tools/stylecheck/st1016\"\n\t\"honnef.co/go/tools/stylecheck/st1017\"\n\t\"honnef.co/go/tools/stylecheck/st1018\"\n\t\"honnef.co/go/tools/stylecheck/st1019\"\n\t\"honnef.co/go/tools/stylecheck/st1020\"\n\t\"honnef.co/go/tools/stylecheck/st1021\"\n\t\"honnef.co/go/tools/stylecheck/st1022\"\n\t\"honnef.co/go/tools/stylecheck/st1023\"\n)\n\nvar Analyzers = []*lint.Analyzer{\n\tst1000.SCAnalyzer,\n\tst1001.SCAnalyzer,\n\tst1003.SCAnalyzer,\n\tst1005.SCAnalyzer,\n\tst1006.SCAnalyzer,\n\tst1008.SCAnalyzer,\n\tst1011.SCAnalyzer,\n\tst1012.SCAnalyzer,\n\tst1013.SCAnalyzer,\n\tst1015.SCAnalyzer,\n\tst1016.SCAnalyzer,\n\tst1017.SCAnalyzer,\n\tst1018.SCAnalyzer,\n\tst1019.SCAnalyzer,\n\tst1020.SCAnalyzer,\n\tst1021.SCAnalyzer,\n\tst1022.SCAnalyzer,\n\tst1023.SCAnalyzer,\n}\n"
  },
  {
    "path": "stylecheck/doc.go",
    "content": "//go:generate go run ../generate.go\n\n// Package stylecheck contains analyzes that enforce style rules.\n// Most of the recommendations made are universally agreed upon by the wider Go community.\n// Some analyzes, however, implement stricter rules that not everyone will agree with.\n// In the context of Staticcheck, these analyzes are not enabled by default.\n//\n// For the most part it is recommended to follow the advice given by the analyzers that are enabled by default,\n// but you may want to disable additional analyzes on a case by case basis.\npackage stylecheck\n"
  },
  {
    "path": "stylecheck/st1000/st1000.go",
    "content": "package st1000\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName: \"ST1000\",\n\t\tRun:  run,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Incorrect or missing package comment`,\n\t\tText: `Packages must have a package comment that is formatted according to\nthe guidelines laid out in\nhttps://go.dev/wiki/CodeReviewComments#package-comments.`,\n\t\tSince:      \"2019.1\",\n\t\tNonDefault: true,\n\t\tMergeIf:    lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// - At least one file in a non-main package should have a package comment\n\t//\n\t// - The comment should be of the form\n\t// \"Package x ...\". This has a slight potential for false\n\t// positives, as multiple files can have package comments, in\n\t// which case they get appended. But that doesn't happen a lot in\n\t// the real world.\n\n\tif pass.Pkg.Name() == \"main\" {\n\t\treturn nil, nil\n\t}\n\thasDocs := false\n\tfor _, f := range pass.Files {\n\t\tif code.IsInTest(pass, f) {\n\t\t\tcontinue\n\t\t}\n\t\ttext, ok := docText(f.Doc)\n\t\tif ok {\n\t\t\thasDocs = true\n\t\t\tprefix := \"Package \" + f.Name.Name\n\t\t\tisNonAlpha := func(b byte) bool {\n\t\t\t\t// This check only considers ASCII, which can lead to false negatives for some malformed package\n\t\t\t\t// comments.\n\t\t\t\treturn !((b >= '0' && b <= '9') || (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z'))\n\t\t\t}\n\t\t\tif !strings.HasPrefix(text, prefix) || (len(text) > len(prefix) && !isNonAlpha(text[len(prefix)])) {\n\t\t\t\treport.Report(pass, f.Doc, fmt.Sprintf(`package comment should be of the form \"%s...\"`, prefix))\n\t\t\t}\n\t\t}\n\t}\n\n\tif !hasDocs {\n\t\tfor _, f := range pass.Files {\n\t\t\tif code.IsInTest(pass, f) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treport.Report(pass, f, \"at least one file in a package should have a package comment\", report.ShortRange())\n\t\t}\n\t}\n\treturn nil, nil\n}\n\nfunc docText(doc *ast.CommentGroup) (string, bool) {\n\tif doc == nil {\n\t\treturn \"\", false\n\t}\n\t// We trim spaces primarily because of /**/ style comments, which often have leading space.\n\ttext := strings.TrimSpace(doc.Text())\n\treturn text, text != \"\"\n}\n"
  },
  {
    "path": "stylecheck/st1000/st1000_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1000\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1000/testdata/go1.0/CheckPackageComment-1/CheckPackageComment-1.go",
    "content": "package pkg //@ diag(`at least one file in a package should have a package comment`)\n"
  },
  {
    "path": "stylecheck/st1000/testdata/go1.0/CheckPackageComment-2/CheckPackageComment-2.go",
    "content": "// This package is great //@ diag(`package comment should be of the form`)\npackage pkg\n"
  },
  {
    "path": "stylecheck/st1000/testdata/go1.0/CheckPackageComment-3/CheckPackageComment-3.go",
    "content": "/*\n   Package pkg is useful.\n*/\npackage pkg\n"
  },
  {
    "path": "stylecheck/st1000/testdata/go1.0/CheckPackageComment-4/CheckPackageComment-4.go",
    "content": "// Package pkg, while not being very long, sure likes punctuation.\npackage pkg\n"
  },
  {
    "path": "stylecheck/st1000/testdata/go1.0/CheckPackageComment-5/CheckPackageComment-5.go",
    "content": "// Package pkg\npackage pkg\n"
  },
  {
    "path": "stylecheck/st1000/testdata/go1.0/CheckPackageComment-6/CheckPackageComment-6.go",
    "content": "// Package pkg2 //@ diag(`package comment should be of the form`)\npackage pkg\n"
  },
  {
    "path": "stylecheck/st1001/st1001.go",
    "content": "package st1001\n\nimport (\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/config\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1001\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{generated.Analyzer, config.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Dot imports are discouraged`,\n\t\tText: `Dot imports that aren't in external test packages are discouraged.\n\nThe \\'dot_import_whitelist\\' option can be used to whitelist certain\nimports.\n\nQuoting Go Code Review Comments:\n\n> The \\'import .\\' form can be useful in tests that, due to circular\n> dependencies, cannot be made part of the package being tested:\n> \n>     package foo_test\n> \n>     import (\n>         \"bar/testutil\" // also imports \"foo\"\n>         . \"foo\"\n>     )\n> \n> In this case, the test file cannot be in package foo because it\n> uses \\'bar/testutil\\', which imports \\'foo\\'. So we use the \\'import .\\'\n> form to let the file pretend to be part of package foo even though\n> it is not. Except for this one case, do not use \\'import .\\' in your\n> programs. It makes the programs much harder to read because it is\n> unclear whether a name like \\'Quux\\' is a top-level identifier in the\n> current package or in an imported package.`,\n\t\tSince:   \"2019.1\",\n\t\tOptions: []string{\"dot_import_whitelist\"},\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, f := range pass.Files {\n\timports:\n\t\tfor _, imp := range f.Imports {\n\t\t\tpath := imp.Path.Value\n\t\t\tpath = path[1 : len(path)-1]\n\t\t\tfor _, w := range config.For(pass).DotImportWhitelist {\n\t\t\t\tif w == path {\n\t\t\t\t\tcontinue imports\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif imp.Name != nil && imp.Name.Name == \".\" && !code.IsInTest(pass, f) {\n\t\t\t\treport.Report(pass, imp, \"should not use dot imports\", report.FilterGenerated())\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "stylecheck/st1001/st1001_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1001\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1001/testdata/go1.0/CheckDotImports/CheckDotImports.go",
    "content": "// Package pkg ...\npackage pkg\n\nimport . \"fmt\" //@ diag(`should not use dot imports`)\n\nvar _ = Println\n"
  },
  {
    "path": "stylecheck/st1001/testdata/go1.0/CheckDotImports/CheckDotImports_test.go",
    "content": "package pkg\n\nimport . \"fmt\"\n\nvar _ = Println\n"
  },
  {
    "path": "stylecheck/st1003/st1003.go",
    "content": "package st1003\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/config\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1003\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer, config.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Poorly chosen identifier`,\n\t\tText: `Identifiers, such as variable and package names, follow certain rules.\n\nSee the following links for details:\n\n- https://go.dev/doc/effective_go#package-names\n- https://go.dev/doc/effective_go#mixed-caps\n- https://go.dev/wiki/CodeReviewComments#initialisms\n- https://go.dev/wiki/CodeReviewComments#variable-names`,\n\t\tSince:      \"2019.1\",\n\t\tNonDefault: true,\n\t\tOptions:    []string{\"initialisms\"},\n\t\tMergeIf:    lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\n// knownNameExceptions is a set of names that are known to be exempt from naming checks.\n// This is usually because they are constrained by having to match names in the\n// standard library.\nvar knownNameExceptions = map[string]bool{\n\t\"LastInsertId\": true, // must match database/sql\n\t\"kWh\":          true,\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\t// A large part of this function is copied from\n\t// github.com/golang/lint, Copyright (c) 2013 The Go Authors,\n\t// licensed under the BSD 3-clause license.\n\n\tallCaps := func(s string) bool {\n\t\thasUppercaseLetters := false\n\t\tfor _, r := range s {\n\t\t\tif !hasUppercaseLetters && r >= 'A' && r <= 'Z' {\n\t\t\t\thasUppercaseLetters = true\n\t\t\t}\n\t\t\tif !((r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn hasUppercaseLetters\n\t}\n\n\tcheck := func(id *ast.Ident, thing string, initialisms map[string]bool) {\n\t\tif id.Name == \"_\" {\n\t\t\treturn\n\t\t}\n\t\tif knownNameExceptions[id.Name] {\n\t\t\treturn\n\t\t}\n\n\t\t// Handle two common styles from other languages that don't belong in Go.\n\t\tif len(id.Name) >= 5 && allCaps(id.Name) && strings.Contains(id.Name, \"_\") {\n\t\t\treport.Report(pass, id, \"should not use ALL_CAPS in Go names; use CamelCase instead\", report.FilterGenerated())\n\t\t\treturn\n\t\t}\n\n\t\tshould := lintName(id.Name, initialisms)\n\t\tif id.Name == should {\n\t\t\treturn\n\t\t}\n\n\t\tif len(id.Name) > 2 && strings.Contains(id.Name[1:len(id.Name)-1], \"_\") {\n\t\t\treport.Report(pass, id, fmt.Sprintf(\"should not use underscores in Go names; %s %s should be %s\", thing, id.Name, should), report.FilterGenerated())\n\t\t\treturn\n\t\t}\n\t\treport.Report(pass, id, fmt.Sprintf(\"%s %s should be %s\", thing, id.Name, should), report.FilterGenerated())\n\t}\n\tcheckList := func(fl *ast.FieldList, thing string, initialisms map[string]bool) {\n\t\tif fl == nil {\n\t\t\treturn\n\t\t}\n\t\tfor _, f := range fl.List {\n\t\t\tfor _, id := range f.Names {\n\t\t\t\tcheck(id, thing, initialisms)\n\t\t\t}\n\t\t}\n\t}\n\n\til := config.For(pass).Initialisms\n\tinitialisms := make(map[string]bool, len(il))\n\tfor _, word := range il {\n\t\tinitialisms[word] = true\n\t}\n\tfor _, f := range pass.Files {\n\t\t// Package names need slightly different handling than other names.\n\t\tif !strings.HasSuffix(f.Name.Name, \"_test\") && strings.Contains(f.Name.Name, \"_\") {\n\t\t\treport.Report(pass, f.Name, \"should not use underscores in package names\", report.FilterGenerated())\n\t\t}\n\t\tif strings.IndexFunc(f.Name.Name, unicode.IsUpper) != -1 {\n\t\t\treport.Report(pass, f.Name, fmt.Sprintf(\"should not use MixedCaps in package name; %s should be %s\", f.Name.Name, strings.ToLower(f.Name.Name)), report.FilterGenerated())\n\t\t}\n\t}\n\n\tfn := func(node ast.Node) {\n\t\tswitch v := node.(type) {\n\t\tcase *ast.AssignStmt:\n\t\t\tif v.Tok != token.DEFINE {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor _, exp := range v.Lhs {\n\t\t\t\tif id, ok := exp.(*ast.Ident); ok {\n\t\t\t\t\tcheck(id, \"var\", initialisms)\n\t\t\t\t}\n\t\t\t}\n\t\tcase *ast.FuncDecl:\n\t\t\t// Functions with no body are defined elsewhere (in\n\t\t\t// assembly, or via go:linkname). These are likely to\n\t\t\t// be something very low level (such as the runtime),\n\t\t\t// where our rules don't apply.\n\t\t\tif v.Body == nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif code.IsInTest(pass, v) &&\n\t\t\t\t(strings.HasPrefix(v.Name.Name, \"Example\") ||\n\t\t\t\t\tstrings.HasPrefix(v.Name.Name, \"Test\") ||\n\t\t\t\t\tstrings.HasPrefix(v.Name.Name, \"Benchmark\") ||\n\t\t\t\t\tstrings.HasPrefix(v.Name.Name, \"Fuzz\")) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthing := \"func\"\n\t\t\tif v.Recv != nil {\n\t\t\t\tthing = \"method\"\n\t\t\t}\n\n\t\t\tif !isTechnicallyExported(v) {\n\t\t\t\tcheck(v.Name, thing, initialisms)\n\t\t\t}\n\n\t\t\tcheckList(v.Type.Params, thing+\" parameter\", initialisms)\n\t\t\tcheckList(v.Type.Results, thing+\" result\", initialisms)\n\t\tcase *ast.GenDecl:\n\t\t\tif v.Tok == token.IMPORT {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvar thing string\n\t\t\tswitch v.Tok {\n\t\t\tcase token.CONST:\n\t\t\t\tthing = \"const\"\n\t\t\tcase token.TYPE:\n\t\t\t\tthing = \"type\"\n\t\t\tcase token.VAR:\n\t\t\t\tthing = \"var\"\n\t\t\t}\n\t\t\tfor _, spec := range v.Specs {\n\t\t\t\tswitch s := spec.(type) {\n\t\t\t\tcase *ast.TypeSpec:\n\t\t\t\t\tcheck(s.Name, thing, initialisms)\n\t\t\t\tcase *ast.ValueSpec:\n\t\t\t\t\tfor _, id := range s.Names {\n\t\t\t\t\t\tcheck(id, thing, initialisms)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tcase *ast.InterfaceType:\n\t\t\t// Do not check interface method names.\n\t\t\t// They are often constrained by the method names of concrete types.\n\t\t\tfor _, x := range v.Methods.List {\n\t\t\t\tft, ok := x.Type.(*ast.FuncType)\n\t\t\t\tif !ok { // might be an embedded interface name\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcheckList(ft.Params, \"interface method parameter\", initialisms)\n\t\t\t\tcheckList(ft.Results, \"interface method result\", initialisms)\n\t\t\t}\n\t\tcase *ast.RangeStmt:\n\t\t\tif v.Tok == token.ASSIGN {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif id, ok := v.Key.(*ast.Ident); ok {\n\t\t\t\tcheck(id, \"range var\", initialisms)\n\t\t\t}\n\t\t\tif id, ok := v.Value.(*ast.Ident); ok {\n\t\t\t\tcheck(id, \"range var\", initialisms)\n\t\t\t}\n\t\tcase *ast.StructType:\n\t\t\tfor _, f := range v.Fields.List {\n\t\t\t\tfor _, id := range f.Names {\n\t\t\t\t\tcheck(id, \"struct field\", initialisms)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tneedle := []ast.Node{\n\t\t(*ast.AssignStmt)(nil),\n\t\t(*ast.FuncDecl)(nil),\n\t\t(*ast.GenDecl)(nil),\n\t\t(*ast.InterfaceType)(nil),\n\t\t(*ast.RangeStmt)(nil),\n\t\t(*ast.StructType)(nil),\n\t}\n\n\tcode.Preorder(pass, fn, needle...)\n\treturn nil, nil\n}\n\n// lintName returns a different name if it should be different.\nfunc lintName(name string, initialisms map[string]bool) (should string) {\n\t// A large part of this function is copied from\n\t// github.com/golang/lint, Copyright (c) 2013 The Go Authors,\n\t// licensed under the BSD 3-clause license.\n\n\t// Fast path for simple cases: \"_\" and all lowercase.\n\tif name == \"_\" {\n\t\treturn name\n\t}\n\tif strings.IndexFunc(name, func(r rune) bool { return !unicode.IsLower(r) }) == -1 {\n\t\treturn name\n\t}\n\n\t// Split camelCase at any lower->upper transition, and split on underscores.\n\t// Check each word for common initialisms.\n\trunes := []rune(name)\n\tw, i := 0, 0 // index of start of word, scan\n\tfor i+1 <= len(runes) {\n\t\teow := false // whether we hit the end of a word\n\t\tif i+1 == len(runes) {\n\t\t\teow = true\n\t\t} else if runes[i+1] == '_' && i+1 != len(runes)-1 {\n\t\t\t// underscore; shift the remainder forward over any run of underscores\n\t\t\teow = true\n\t\t\tn := 1\n\t\t\tfor i+n+1 < len(runes) && runes[i+n+1] == '_' {\n\t\t\t\tn++\n\t\t\t}\n\n\t\t\t// Leave at most one underscore if the underscore is between two digits\n\t\t\tif i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {\n\t\t\t\tn--\n\t\t\t}\n\n\t\t\tcopy(runes[i+1:], runes[i+n+1:])\n\t\t\trunes = runes[:len(runes)-n]\n\t\t} else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {\n\t\t\t// lower->non-lower\n\t\t\teow = true\n\t\t}\n\t\ti++\n\t\tif !eow {\n\t\t\tcontinue\n\t\t}\n\n\t\t// [w,i) is a word.\n\t\tword := string(runes[w:i])\n\t\tif u := strings.ToUpper(word); initialisms[u] {\n\t\t\t// Keep consistent case, which is lowercase only at the start.\n\t\t\tif w == 0 && unicode.IsLower(runes[w]) {\n\t\t\t\tu = strings.ToLower(u)\n\t\t\t}\n\t\t\t// All the common initialisms are ASCII,\n\t\t\t// so we can replace the bytes exactly.\n\t\t\t// TODO(dh): this won't be true once we allow custom initialisms\n\t\t\tcopy(runes[w:], []rune(u))\n\t\t} else if w > 0 && strings.ToLower(word) == word {\n\t\t\t// already all lowercase, and not the first word, so uppercase the first character.\n\t\t\trunes[w] = unicode.ToUpper(runes[w])\n\t\t}\n\t\tw = i\n\t}\n\treturn string(runes)\n}\n\nfunc isTechnicallyExported(f *ast.FuncDecl) bool {\n\tif f.Recv != nil || f.Doc == nil {\n\t\treturn false\n\t}\n\n\tconst export = \"//export \"\n\tconst linkname = \"//go:linkname \"\n\tfor _, c := range f.Doc.List {\n\t\tif strings.HasPrefix(c.Text, export) && len(c.Text) == len(export)+len(f.Name.Name) && c.Text[len(export):] == f.Name.Name {\n\t\t\treturn true\n\t\t}\n\n\t\tif strings.HasPrefix(c.Text, linkname) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "stylecheck/st1003/st1003_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1003\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1003/testdata/go1.0/CheckNames/CheckNames.go",
    "content": "// Package pkg_foo ...\npackage pkg_foo //@ diag(`should not use underscores in package names`)\n\nimport _ \"unsafe\"\n\nvar range_ int\nvar _abcdef int\nvar abcdef_ int\nvar abc_def int  //@ diag(`should not use underscores in Go names; var abc_def should be abcDef`)\nvar abc_def_ int //@ diag(`should not use underscores in Go names; var abc_def_ should be abcDef_`)\n\nfunc fn_1()  {} //@ diag(`func fn_1 should be fn1`)\nfunc fn2()   {}\nfunc fn_Id() {} //@ diag(`func fn_Id should be fnID`)\nfunc fnId()  {} //@ diag(`func fnId should be fnID`)\n\nvar FOO_BAR int //@ diag(`should not use ALL_CAPS in Go names; use CamelCase instead`)\nvar Foo_BAR int //@ diag(`var Foo_BAR should be FooBAR`)\nvar foo_bar int //@ diag(`foo_bar should be fooBar`)\nvar kFoobar int // not a check we inherited from golint. more false positives than true ones.\n\nvar _1000 int // issue 858\n\nfunc fn(x []int) {\n\tvar (\n\t\ta_b = 1 //@ diag(`var a_b should be aB`)\n\t\tc_d int //@ diag(`var c_d should be cD`)\n\t)\n\ta_b += 2\n\tfor e_f := range x { //@ diag(`range var e_f should be eF`)\n\t\t_ = e_f\n\t}\n\n\t_ = a_b\n\t_ = c_d\n}\n\n//export fn_3\nfunc fn_3() {}\n\n//export not actually the export keyword\nfunc fn_4() {} //@ diag(`func fn_4 should be fn4`)\n\n//export\nfunc fn_5() {} //@ diag(`func fn_5 should be fn5`)\n\n// export fn_6\nfunc fn_6() {} //@ diag(`func fn_6 should be fn6`)\n\n//export fn_8\nfunc fn_7() {} //@ diag(`func fn_7 should be fn7`)\n\n//go:linkname fn_8 time.Now\nfunc fn_8() {}\n"
  },
  {
    "path": "stylecheck/st1003/testdata/go1.0/CheckNames_generated/CheckNames_generated.go",
    "content": "// Code generated by an actual human. DO NOT EDIT.\n\n// Package pkg_foo ...\npackage pkg_foo\n"
  },
  {
    "path": "stylecheck/st1005/st1005.go",
    "content": "package st1005\n\nimport (\n\t\"go/constant\"\n\t\"strings\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ir\"\n\t\"honnef.co/go/tools/go/ir/irutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1005\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Incorrectly formatted error string`,\n\t\tText: `Error strings follow a set of guidelines to ensure uniformity and good\ncomposability.\n\nQuoting Go Code Review Comments:\n\n> Error strings should not be capitalized (unless beginning with\n> proper nouns or acronyms) or end with punctuation, since they are\n> usually printed following other context. That is, use\n> \\'fmt.Errorf(\"something bad\")\\' not \\'fmt.Errorf(\"Something bad\")\\', so\n> that \\'log.Printf(\"Reading %s: %v\", filename, err)\\' formats without a\n> spurious capital letter mid-message.`,\n\t\tSince:   \"2019.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tobjNames := map[*ir.Package]map[string]bool{}\n\tirpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg\n\tobjNames[irpkg] = map[string]bool{}\n\tfor _, m := range irpkg.Members {\n\t\tif typ, ok := m.(*ir.Type); ok {\n\t\t\tobjNames[irpkg][typ.Name()] = true\n\t\t}\n\t}\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tobjNames[fn.Package()][fn.Name()] = true\n\t}\n\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tif code.IsInTest(pass, fn) {\n\t\t\t// We don't care about malformed error messages in tests;\n\t\t\t// they're usually for direct human consumption, not part\n\t\t\t// of an API\n\t\t\tcontinue\n\t\t}\n\t\tfor _, block := range fn.Blocks {\n\t\tinstrLoop:\n\t\t\tfor _, ins := range block.Instrs {\n\t\t\t\tcall, ok := ins.(*ir.Call)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !irutil.IsCallToAny(call.Common(), \"errors.New\", \"fmt.Errorf\") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tk, ok := call.Common().Args[0].(*ir.Const)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\ts := constant.StringVal(k.Value)\n\t\t\t\tif len(s) == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tswitch s[len(s)-1] {\n\t\t\t\tcase '.', ':', '!', '\\n':\n\t\t\t\t\treport.Report(pass, call, \"error strings should not end with punctuation or newlines\")\n\t\t\t\t}\n\t\t\t\tbefore, _, ok0 := strings.Cut(s, \" \")\n\t\t\t\tif !ok0 {\n\t\t\t\t\t// single word error message, probably not a real\n\t\t\t\t\t// error but something used in tests or during\n\t\t\t\t\t// debugging\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tword := before\n\t\t\t\tfirst, n := utf8.DecodeRuneInString(word)\n\t\t\t\tif !unicode.IsUpper(first) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfor _, c := range word[n:] {\n\t\t\t\t\tif unicode.IsUpper(c) || unicode.IsDigit(c) {\n\t\t\t\t\t\t// Word is probably an initialism or multi-word function name. Digits cover elliptic curves like\n\t\t\t\t\t\t// P384.\n\t\t\t\t\t\tcontinue instrLoop\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif strings.ContainsRune(word, '(') {\n\t\t\t\t\t// Might be a function call\n\t\t\t\t\tcontinue instrLoop\n\t\t\t\t}\n\t\t\t\tword = strings.TrimRightFunc(word, func(r rune) bool { return unicode.IsPunct(r) })\n\t\t\t\tif objNames[fn.Package()][word] {\n\t\t\t\t\t// Word is probably the name of a function or type in this package\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// First word in error starts with a capital\n\t\t\t\t// letter, and the word doesn't contain any other\n\t\t\t\t// capitals, making it unlikely to be an\n\t\t\t\t// initialism or multi-word function name.\n\t\t\t\t//\n\t\t\t\t// It could still be a proper noun, though.\n\n\t\t\t\treport.Report(pass, call, \"error strings should not be capitalized\")\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "stylecheck/st1005/st1005_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1005\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1005/testdata/go1.0/CheckErrorStrings/CheckErrorStrings.go",
    "content": "// Package pkg ...\npackage pkg\n\nimport \"errors\"\n\nfunc fn() {\n\terrors.New(\"a perfectly fine error\")\n\terrors.New(\"Not a great error\")       //@ diag(`error strings should not be capitalized`)\n\terrors.New(\"also not a great error.\") //@ diag(`error strings should not end with punctuation or newlines`)\n\terrors.New(\"URL is okay\")\n\terrors.New(\"SomeFunc is okay\")\n\terrors.New(\"URL is okay, but the period is not.\") //@ diag(`error strings should not end with punctuation or newlines`)\n\terrors.New(\"T must not be nil\")\n\terrors.New(\"Foo() failed\")\n\terrors.New(\"Foo(bar) failed\")\n\terrors.New(\"Foo(bar, baz) failed\")\n\terrors.New(\"P384 is a nice curve\")\n}\n\nfunc Write() {\n\terrors.New(\"Write: this is broken\")\n}\n\ntype T struct{}\n\nfunc (T) Read() {\n\terrors.New(\"Read: this is broken\")\n\terrors.New(\"Read failed\")\n}\n\nfunc fn2() {\n\t// The error string hasn't to be in the same function\n\terrors.New(\"Read failed\")\n}\n"
  },
  {
    "path": "stylecheck/st1006/st1006.go",
    "content": "package st1006\n\nimport (\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1006\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Poorly chosen receiver name`,\n\t\tText: `Quoting Go Code Review Comments:\n\n> The name of a method's receiver should be a reflection of its\n> identity; often a one or two letter abbreviation of its type\n> suffices (such as \"c\" or \"cl\" for \"Client\"). Don't use generic\n> names such as \"me\", \"this\" or \"self\", identifiers typical of\n> object-oriented languages that place more emphasis on methods as\n> opposed to functions. The name need not be as descriptive as that\n> of a method argument, as its role is obvious and serves no\n> documentary purpose. It can be very short as it will appear on\n> almost every line of every method of the type; familiarity admits\n> brevity. Be consistent, too: if you call the receiver \"c\" in one\n> method, don't call it \"cl\" in another.`,\n\t\tSince:   \"2019.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tirpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg\n\tfor _, m := range irpkg.Members {\n\t\tif T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() {\n\t\t\tms := typeutil.IntuitiveMethodSet(T.Type(), nil)\n\t\t\tfor _, sel := range ms {\n\t\t\t\tfn := sel.Obj().(*types.Func)\n\t\t\t\trecv := fn.Type().(*types.Signature).Recv()\n\t\t\t\tif typeutil.Dereference(recv.Type()) != T.Type() {\n\t\t\t\t\t// skip embedded methods\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif recv.Name() == \"self\" || recv.Name() == \"this\" {\n\t\t\t\t\treport.Report(pass, recv, `receiver name should be a reflection of its identity; don't use generic names such as \"this\" or \"self\"`, report.FilterGenerated())\n\t\t\t\t}\n\t\t\t\tif recv.Name() == \"_\" {\n\t\t\t\t\treport.Report(pass, recv, \"receiver name should not be an underscore, omit the name if it is unused\", report.FilterGenerated())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "stylecheck/st1006/st1006_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1006\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1006/testdata/go1.0/CheckReceiverNames/CheckReceiverNames.go",
    "content": "// Package pkg ...\npackage pkg\n\ntype T1 int\n\nfunc (x T1) Fn1()    {}\nfunc (y T1) Fn2()    {}\nfunc (x T1) Fn3()    {}\nfunc (T1) Fn4()      {}\nfunc (_ T1) Fn5()    {} //@ diag(`receiver name should not be an underscore, omit the name if it is unused`)\nfunc (self T1) Fn6() {} //@ diag(`receiver name should be a reflection of its identity`)\n"
  },
  {
    "path": "stylecheck/st1008/st1008.go",
    "content": "package st1008\n\nimport (\n\t\"go/types\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1008\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:   `A function's error value should be its last return value`,\n\t\tText:    `A function's error value should be its last return value.`,\n\t\tSince:   `2019.1`,\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\nfnLoop:\n\tfor _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {\n\t\tsig := fn.Type().(*types.Signature)\n\t\trets := sig.Results()\n\t\tif rets == nil || rets.Len() < 2 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif types.Unalias(rets.At(rets.Len()-1).Type()) == types.Universe.Lookup(\"error\").Type() {\n\t\t\t// Last return type is error. If the function also returns\n\t\t\t// errors in other positions, that's fine.\n\t\t\tcontinue\n\t\t}\n\n\t\tif rets.Len() >= 2 &&\n\t\t\ttypes.Unalias(rets.At(rets.Len()-1).Type()) == types.Universe.Lookup(\"bool\").Type() &&\n\t\t\ttypes.Unalias(rets.At(rets.Len()-2).Type()) == types.Universe.Lookup(\"error\").Type() {\n\t\t\t// Accept (..., error, bool) and assume it's a comma-ok function. It's not clear whether the bool should come last or not for these kinds of functions.\n\t\t\tcontinue\n\t\t}\n\t\tfor i := rets.Len() - 2; i >= 0; i-- {\n\t\t\tif types.Unalias(rets.At(i).Type()) == types.Universe.Lookup(\"error\").Type() {\n\t\t\t\treport.Report(pass, rets.At(i), \"error should be returned as the last argument\", report.ShortRange())\n\t\t\t\tcontinue fnLoop\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "stylecheck/st1008/st1008_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1008\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1008/testdata/go1.0/CheckErrorReturn/CheckErrorReturn.go",
    "content": "// Package pkg ...\npackage pkg\n\nfunc fn1() (error, int)        { return nil, 0 }      //@ diag(`error should be returned as the last argument`)\nfunc fn2() (a, b error, c int) { return nil, nil, 0 } //@ diag(`error should be returned as the last argument`)\nfunc fn3() (a int, b, c error) { return 0, nil, nil }\nfunc fn4() (error, error)      { return nil, nil }\nfunc fn5() int                 { return 0 }\nfunc fn6() (int, error)        { return 0, nil }\nfunc fn7() (error, int, error) { return nil, 0, nil }\n\n// it's not clear if the error should come first or second in a function that also has a comma-ok return value\nfunc fn8() (error, bool)      { return nil, false }\nfunc fn9() (int, error, bool) { return 0, nil, false }\n"
  },
  {
    "path": "stylecheck/st1008/testdata/go1.9/CheckErrorReturn/CheckErrorReturn.go",
    "content": "// Package pkg ...\npackage pkg\n\ntype Alias = error\n\nfunc fn10() (Alias, int) { return nil, 0 } //@ diag(`error should be returned as the last argument`)\n"
  },
  {
    "path": "stylecheck/st1011/st1011.go",
    "content": "package st1011\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1011\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Poorly chosen name for variable of type \\'time.Duration\\'`,\n\t\tText: `\\'time.Duration\\' values represent an amount of time, which is represented\nas a count of nanoseconds. An expression like \\'5 * time.Microsecond\\'\nyields the value \\'5000\\'. It is therefore not appropriate to suffix a\nvariable of type \\'time.Duration\\' with any time unit, such as \\'Msec\\' or\n\\'Milli\\'.`,\n\t\tSince:   `2019.1`,\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tsuffixes := []string{\n\t\t\"Sec\", \"Secs\", \"Seconds\",\n\t\t\"Msec\", \"Msecs\",\n\t\t\"Milli\", \"Millis\", \"Milliseconds\",\n\t\t\"Usec\", \"Usecs\", \"Microseconds\",\n\t\t\"MS\", \"Ms\",\n\t}\n\tfn := func(names []*ast.Ident) {\n\t\tfor _, name := range names {\n\t\t\tif _, ok := pass.TypesInfo.Defs[name]; !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tT := pass.TypesInfo.TypeOf(name)\n\t\t\tif !typeutil.IsTypeWithName(T, \"time.Duration\") && !typeutil.IsPointerToTypeWithName(T, \"time.Duration\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor _, suffix := range suffixes {\n\t\t\t\tif strings.HasSuffix(name.Name, suffix) {\n\t\t\t\t\treport.Report(pass, name, fmt.Sprintf(\"var %s is of type %v; don't use unit-specific suffix %q\", name.Name, T, suffix))\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfn2 := func(node ast.Node) {\n\t\tswitch node := node.(type) {\n\t\tcase *ast.ValueSpec:\n\t\t\tfn(node.Names)\n\t\tcase *ast.FieldList:\n\t\t\tfor _, field := range node.List {\n\t\t\t\tfn(field.Names)\n\t\t\t}\n\t\tcase *ast.AssignStmt:\n\t\t\tif node.Tok != token.DEFINE {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tvar names []*ast.Ident\n\t\t\tfor _, lhs := range node.Lhs {\n\t\t\t\tif lhs, ok := lhs.(*ast.Ident); ok {\n\t\t\t\t\tnames = append(names, lhs)\n\t\t\t\t}\n\t\t\t}\n\t\t\tfn(names)\n\t\t}\n\t}\n\n\tcode.Preorder(pass, fn2, (*ast.ValueSpec)(nil), (*ast.FieldList)(nil), (*ast.AssignStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "stylecheck/st1011/st1011_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1011\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1011/testdata/go1.0/CheckTimeNames/CheckTimeNames.go",
    "content": "// Package pkg ...\npackage pkg\n\nimport \"time\"\n\ntype T1 struct {\n\taMS     int\n\tB       time.Duration\n\tBMillis time.Duration //@ diag(`don't use unit-specific suffix`)\n}\n\nfunc fn1(a, b, cMS time.Duration) { //@ diag(`don't use unit-specific suffix`)\n\tvar x time.Duration\n\tvar xMS time.Duration    //@ diag(`don't use unit-specific suffix`)\n\tvar y, yMS time.Duration //@ diag(`don't use unit-specific suffix`)\n\tvar zMS = time.Second    //@ diag(`don't use unit-specific suffix`)\n\taMS := time.Second       //@ diag(`don't use unit-specific suffix`)\n\tunrelated, aMS := 0, 0\n\taMS, bMS := 0, time.Second //@ diag(re`var bMS .+ don't use unit-specific suffix`)\n\n\t_, _, _, _, _, _, _, _ = x, xMS, y, yMS, zMS, aMS, unrelated, bMS\n}\n"
  },
  {
    "path": "stylecheck/st1012/st1012.go",
    "content": "package st1012\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName: \"ST1012\",\n\t\tRun:  run,\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Poorly chosen name for error variable`,\n\t\tText: `Error variables that are part of an API should be called \\'errFoo\\' or\n\\'ErrFoo\\'.`,\n\t\tSince:   \"2019.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, f := range pass.Files {\n\t\tfor _, decl := range f.Decls {\n\t\t\tgen, ok := decl.(*ast.GenDecl)\n\t\t\tif !ok || gen.Tok != token.VAR {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor _, spec := range gen.Specs {\n\t\t\t\tspec := spec.(*ast.ValueSpec)\n\t\t\t\tif len(spec.Names) != len(spec.Values) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tfor i, name := range spec.Names {\n\t\t\t\t\tval := spec.Values[i]\n\t\t\t\t\tif !code.IsCallToAny(pass, val, \"errors.New\", \"fmt.Errorf\") {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tif pass.Pkg.Path() == \"net/http\" && strings.HasPrefix(name.Name, \"http2err\") {\n\t\t\t\t\t\t// special case for internal variable names of\n\t\t\t\t\t\t// bundled HTTP 2 code in net/http\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tprefix := \"err\"\n\t\t\t\t\tif name.IsExported() {\n\t\t\t\t\t\tprefix = \"Err\"\n\t\t\t\t\t}\n\t\t\t\t\tif !strings.HasPrefix(name.Name, prefix) {\n\t\t\t\t\t\treport.Report(pass, name, fmt.Sprintf(\"error var %s should have name of the form %sFoo\", name.Name, prefix))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "stylecheck/st1012/st1012_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1012\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1012/testdata/go1.0/CheckErrorVarNames/CheckErrorVarNames.go",
    "content": "// Package pkg ...\npackage pkg\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nvar (\n\tfoo                   = errors.New(\"\") //@ diag(`error var foo should have name of the form errFoo`)\n\terrBar                = errors.New(\"\")\n\tqux, fisk, errAnother = errors.New(\"\"), errors.New(\"\"), errors.New(\"\") //@ diag(`error var qux should have name of the form errFoo`), diag(`error var fisk should have name of the form errFoo`)\n\tabc                   = fmt.Errorf(\"\")                                 //@ diag(`error var abc should have name of the form errFoo`)\n\n\terrAbc = fmt.Errorf(\"\")\n)\n\nvar wrong = errors.New(\"\") //@ diag(`error var wrong should have name of the form errFoo`)\n\nvar result = fn()\n\nfunc fn() error { return nil }\n"
  },
  {
    "path": "stylecheck/st1013/st1013.go",
    "content": "package st1013\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/constant\"\n\t\"strconv\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/config\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1013\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer, config.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Should use constants for HTTP error codes, not magic numbers`,\n\t\tText: `HTTP has a tremendous number of status codes. While some of those are\nwell known (200, 400, 404, 500), most of them are not. The \\'net/http\\'\npackage provides constants for all status codes that are part of the\nvarious specifications. It is recommended to use these constants\ninstead of hard-coding magic numbers, to vastly improve the\nreadability of your code.`,\n\t\tSince:   \"2019.1\",\n\t\tOptions: []string{\"http_status_code_whitelist\"},\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar query = pattern.MustParse(`\n\t(CallExpr\n\t\t(Symbol\n\t\t\tname@(Or\n\t\t\t\"net/http.Error\"\n\t\t\t\"net/http.Redirect\"\n\t\t\t\"net/http.StatusText\"\n\t\t\t\"net/http.RedirectHandler\"))\n\t\targs)`)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\twhitelist := map[string]bool{}\n\tfor _, code := range config.For(pass).HTTPStatusCodeWhitelist {\n\t\twhitelist[code] = true\n\t}\n\tfor _, m := range code.Matches(pass, query) {\n\t\tvar arg int\n\t\tswitch m.State[\"name\"].(string) {\n\t\tcase \"net/http.Error\":\n\t\t\targ = 2\n\t\tcase \"net/http.Redirect\":\n\t\t\targ = 3\n\t\tcase \"net/http.StatusText\":\n\t\t\targ = 0\n\t\tcase \"net/http.RedirectHandler\":\n\t\t\targ = 1\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\t\targs := m.State[\"args\"].([]ast.Expr)\n\t\tif arg >= len(args) {\n\t\t\tcontinue\n\t\t}\n\t\ttv, ok := code.IntegerLiteral(pass, args[arg])\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tn, ok := constant.Int64Val(tv.Value)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tif whitelist[strconv.FormatInt(n, 10)] {\n\t\t\tcontinue\n\t\t}\n\n\t\ts, ok := httpStatusCodes[n]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tlit := args[arg]\n\t\treport.Report(pass, lit, fmt.Sprintf(\"should use constant http.%s instead of numeric literal %d\", s, n),\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(fmt.Sprintf(\"Use http.%s instead of %d\", s, n), edit.ReplaceWithString(lit, \"http.\"+s))))\n\t}\n\treturn nil, nil\n}\n\nvar httpStatusCodes = map[int64]string{\n\t100: \"StatusContinue\",\n\t101: \"StatusSwitchingProtocols\",\n\t102: \"StatusProcessing\",\n\t200: \"StatusOK\",\n\t201: \"StatusCreated\",\n\t202: \"StatusAccepted\",\n\t203: \"StatusNonAuthoritativeInfo\",\n\t204: \"StatusNoContent\",\n\t205: \"StatusResetContent\",\n\t206: \"StatusPartialContent\",\n\t207: \"StatusMultiStatus\",\n\t208: \"StatusAlreadyReported\",\n\t226: \"StatusIMUsed\",\n\t300: \"StatusMultipleChoices\",\n\t301: \"StatusMovedPermanently\",\n\t302: \"StatusFound\",\n\t303: \"StatusSeeOther\",\n\t304: \"StatusNotModified\",\n\t305: \"StatusUseProxy\",\n\t307: \"StatusTemporaryRedirect\",\n\t308: \"StatusPermanentRedirect\",\n\t400: \"StatusBadRequest\",\n\t401: \"StatusUnauthorized\",\n\t402: \"StatusPaymentRequired\",\n\t403: \"StatusForbidden\",\n\t404: \"StatusNotFound\",\n\t405: \"StatusMethodNotAllowed\",\n\t406: \"StatusNotAcceptable\",\n\t407: \"StatusProxyAuthRequired\",\n\t408: \"StatusRequestTimeout\",\n\t409: \"StatusConflict\",\n\t410: \"StatusGone\",\n\t411: \"StatusLengthRequired\",\n\t412: \"StatusPreconditionFailed\",\n\t413: \"StatusRequestEntityTooLarge\",\n\t414: \"StatusRequestURITooLong\",\n\t415: \"StatusUnsupportedMediaType\",\n\t416: \"StatusRequestedRangeNotSatisfiable\",\n\t417: \"StatusExpectationFailed\",\n\t418: \"StatusTeapot\",\n\t422: \"StatusUnprocessableEntity\",\n\t423: \"StatusLocked\",\n\t424: \"StatusFailedDependency\",\n\t426: \"StatusUpgradeRequired\",\n\t428: \"StatusPreconditionRequired\",\n\t429: \"StatusTooManyRequests\",\n\t431: \"StatusRequestHeaderFieldsTooLarge\",\n\t451: \"StatusUnavailableForLegalReasons\",\n\t500: \"StatusInternalServerError\",\n\t501: \"StatusNotImplemented\",\n\t502: \"StatusBadGateway\",\n\t503: \"StatusServiceUnavailable\",\n\t504: \"StatusGatewayTimeout\",\n\t505: \"StatusHTTPVersionNotSupported\",\n\t506: \"StatusVariantAlsoNegotiates\",\n\t507: \"StatusInsufficientStorage\",\n\t508: \"StatusLoopDetected\",\n\t510: \"StatusNotExtended\",\n\t511: \"StatusNetworkAuthenticationRequired\",\n}\n"
  },
  {
    "path": "stylecheck/st1013/st1013_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1013\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1013/testdata/go1.0/CheckHTTPStatusCodes/CheckHTTPStatusCodes.go",
    "content": "// Package pkg ...\npackage pkg\n\nimport \"net/http\"\n\nfunc fn() {\n\t// Check all the supported functions\n\thttp.Error(nil, \"\", 506)         //@ diag(`http.StatusVariantAlsoNegotiates`)\n\thttp.Redirect(nil, nil, \"\", 506) //@ diag(`http.StatusVariantAlsoNegotiates`)\n\thttp.StatusText(506)             //@ diag(`http.StatusVariantAlsoNegotiates`)\n\thttp.RedirectHandler(\"\", 506)    //@ diag(`http.StatusVariantAlsoNegotiates`)\n\n\t// Don't flag literals with no known constant\n\thttp.StatusText(600)\n\n\t// Don't flag constants\n\thttp.StatusText(http.StatusAccepted)\n\n\t// Don't flag items on the whitelist (well known codes)\n\thttp.StatusText(404)\n\n\thttp.Error(fn2())\n}\n\nfunc fn2() (http.ResponseWriter, string, int) { return nil, \"\", 0 }\n"
  },
  {
    "path": "stylecheck/st1013/testdata/go1.0/CheckHTTPStatusCodes/CheckHTTPStatusCodes.go.golden",
    "content": "// Package pkg ...\npackage pkg\n\nimport \"net/http\"\n\nfunc fn() {\n\t// Check all the supported functions\n\thttp.Error(nil, \"\", http.StatusVariantAlsoNegotiates)         //@ diag(`http.StatusVariantAlsoNegotiates`)\n\thttp.Redirect(nil, nil, \"\", http.StatusVariantAlsoNegotiates) //@ diag(`http.StatusVariantAlsoNegotiates`)\n\thttp.StatusText(http.StatusVariantAlsoNegotiates)             //@ diag(`http.StatusVariantAlsoNegotiates`)\n\thttp.RedirectHandler(\"\", http.StatusVariantAlsoNegotiates)    //@ diag(`http.StatusVariantAlsoNegotiates`)\n\n\t// Don't flag literals with no known constant\n\thttp.StatusText(600)\n\n\t// Don't flag constants\n\thttp.StatusText(http.StatusAccepted)\n\n\t// Don't flag items on the whitelist (well known codes)\n\thttp.StatusText(404)\n\n\thttp.Error(fn2())\n}\n\nfunc fn2() (http.ResponseWriter, string, int) { return nil, \"\", 0 }\n"
  },
  {
    "path": "stylecheck/st1015/st1015.go",
    "content": "package st1015\n\nimport (\n\t\"go/ast\"\n\t\"go/token\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1015\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:   `A switch's default case should be the first or last case`,\n\t\tSince:   \"2019.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\thasFallthrough := func(clause ast.Stmt) bool {\n\t\t// A valid fallthrough statement may be used only as the final non-empty statement in a case clause. Thus we can\n\t\t// easily avoid falsely matching fallthroughs in nested switches by not descending into blocks.\n\n\t\tbody := clause.(*ast.CaseClause).Body\n\t\tfor i := len(body) - 1; i >= 0; i-- {\n\t\t\tlast := body[i]\n\t\t\tswitch stmt := last.(type) {\n\t\t\tcase *ast.EmptyStmt:\n\t\t\t\t// Fallthrough may be followed by empty statements\n\t\t\tcase *ast.BranchStmt:\n\t\t\t\treturn stmt.Tok == token.FALLTHROUGH\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\treturn false\n\t}\n\n\tfn := func(node ast.Node) {\n\t\tstmt := node.(*ast.SwitchStmt)\n\t\tlist := stmt.Body.List\n\t\tdefaultIdx := -1\n\t\tfor i, c := range list {\n\t\t\tif c.(*ast.CaseClause).List == nil {\n\t\t\t\tdefaultIdx = i\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif defaultIdx == -1 || defaultIdx == 0 || defaultIdx == len(list)-1 {\n\t\t\t// No default case, or it's the first or last case\n\t\t\treturn\n\t\t}\n\n\t\tif hasFallthrough(list[defaultIdx-1]) || hasFallthrough(list[defaultIdx]) {\n\t\t\t// We either fall into or out of this case; don't mess with the order\n\t\t\treturn\n\t\t}\n\n\t\treport.Report(pass, list[defaultIdx], \"default case should be first or last in switch statement\", report.FilterGenerated())\n\t}\n\tcode.Preorder(pass, fn, (*ast.SwitchStmt)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "stylecheck/st1015/st1015_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1015\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1015/testdata/go1.0/CheckDefaultCaseOrder/CheckDefaultCaseOrder.go",
    "content": "// Package pkg ...\npackage pkg\n\nfunc fn(x int) {\n\tswitch x {\n\t}\n\tswitch x {\n\tcase 1:\n\t}\n\n\tswitch x {\n\tcase 1:\n\tcase 2:\n\tcase 3:\n\t}\n\n\tswitch x {\n\tdefault:\n\t}\n\n\tswitch x {\n\tdefault:\n\tcase 1:\n\t}\n\n\tswitch x {\n\tcase 1:\n\tdefault:\n\t}\n\n\tswitch x {\n\tcase 1:\n\tdefault: //@ diag(`default case should be first or last in switch statement`)\n\tcase 2:\n\t}\n\n\t// Don't flag either of these two; fallthrough is sensitive to the order of cases\n\tswitch x {\n\tcase 1:\n\t\tfallthrough\n\tdefault:\n\tcase 2:\n\t}\n\tswitch x {\n\tcase 1:\n\tdefault:\n\t\tfallthrough\n\tcase 2:\n\t}\n\n\t// Do flag these branches; don't get confused by the nested switch statements\n\tswitch x {\n\tcase 1:\n\t\tfunc() {\n\t\t\tswitch x {\n\t\t\tcase 1:\n\t\t\t\tfallthrough\n\t\t\tcase 2:\n\t\t\t}\n\t\t}()\n\tdefault: //@ diag(`default case should be first or last in switch statement`)\n\tcase 3:\n\t}\n\n\tswitch x {\n\tcase 1:\n\t\tswitch x {\n\t\tcase 1:\n\t\t\tfallthrough\n\t\tcase 2:\n\t\t}\n\tdefault: //@ diag(`default case should be first or last in switch statement`)\n\tcase 3:\n\t}\n\n\t// Fallthrough may be followed by empty statements\n\tswitch x {\n\tcase 1:\n\t\tfallthrough\n\t\t;\n\tdefault:\n\tcase 3:\n\t}\n}\n"
  },
  {
    "path": "stylecheck/st1016/st1016.go",
    "content": "package st1016\n\nimport (\n\t\"fmt\"\n\t\"go/types\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\t\"honnef.co/go/tools/internal/passes/buildir\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1016\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:      `Use consistent method receiver names`,\n\t\tSince:      \"2019.1\",\n\t\tNonDefault: true,\n\t\tMergeIf:    lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tirpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg\n\tfor _, m := range irpkg.Members {\n\t\tnames := map[string]int{}\n\n\t\tvar firstFn *types.Func\n\t\tif T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() {\n\t\t\tms := typeutil.IntuitiveMethodSet(T.Type(), nil)\n\t\t\tfor _, sel := range ms {\n\t\t\t\tfn := sel.Obj().(*types.Func)\n\t\t\t\trecv := fn.Type().(*types.Signature).Recv()\n\t\t\t\tif code.IsGenerated(pass, recv.Pos()) {\n\t\t\t\t\t// Don't concern ourselves with methods in generated code\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif typeutil.Dereference(recv.Type()) != T.Type() {\n\t\t\t\t\t// skip embedded methods\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif firstFn == nil {\n\t\t\t\t\tfirstFn = fn\n\t\t\t\t}\n\t\t\t\tif recv.Name() != \"\" && recv.Name() != \"_\" {\n\t\t\t\t\tnames[recv.Name()]++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif len(names) > 1 {\n\t\t\tvar seen []string\n\t\t\tfor name, count := range names {\n\t\t\t\tseen = append(seen, fmt.Sprintf(\"%dx %q\", count, name))\n\t\t\t}\n\t\t\tsort.Strings(seen)\n\n\t\t\treport.Report(pass, firstFn, fmt.Sprintf(\"methods on the same type should have the same receiver name (seen %s)\", strings.Join(seen, \", \")))\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "stylecheck/st1016/st1016_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1016\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1016/testdata/go1.0/CheckReceiverNamesIdentical/CheckReceiverNames.go",
    "content": "// Package pkg ...\npackage pkg\n\ntype T1 int\n\nfunc (x T1) Fn1()    {} //@ diag(`methods on the same type should have the same receiver name`)\nfunc (y T1) Fn2()    {}\nfunc (x T1) Fn3()    {}\nfunc (T1) Fn4()      {}\nfunc (_ T1) Fn5()    {}\nfunc (self T1) Fn6() {}\n\nfunc (bar T3) Fn2()  {} //@ diag(`1x \"bar\", 1x \"meow\"`)\nfunc (meow T3) Fn3() {}\n\nfunc (bar T4) Fn2() {}\n"
  },
  {
    "path": "stylecheck/st1016/testdata/go1.0/CheckReceiverNamesIdentical/generated.go",
    "content": "// Code generated by hand. DO NOT EDIT.\n\npackage pkg\n\n// Methods on T2 are only defined in this generated file\n// Methods on T3 and T4 are defined in this file and a non-generated file\n\ntype T2 struct{}\n\nfunc (foo T2) Fn1() {}\nfunc (bar T2) Fn2() {}\n\ntype T3 struct{}\n\nfunc (foo T3) Fn1() {}\n\ntype T4 struct{}\n\nfunc (foo T4) Fn1() {}\n"
  },
  {
    "path": "stylecheck/st1017/st1017.go",
    "content": "package st1017\n\nimport (\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/edit\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/pattern\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1017\",\n\t\tRun:      run,\n\t\tRequires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Don't use Yoda conditions`,\n\t\tText: `Yoda conditions are conditions of the kind \\\"if 42 == x\\\", where the\nliteral is on the left side of the comparison. These are a common\nidiom in languages in which assignment is an expression, to avoid bugs\nof the kind \\\"if (x = 42)\\\". In Go, which doesn't allow for this kind of\nbug, we prefer the more idiomatic \\\"if x == 42\\\".`,\n\t\tSince:   \"2019.2\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nvar (\n\tcheckYodaConditionsQ = pattern.MustParse(`(BinaryExpr left@(TrulyConstantExpression _) tok@(Or \"==\" \"!=\") right@(Not (TrulyConstantExpression _)))`)\n\tcheckYodaConditionsR = pattern.MustParse(`(BinaryExpr right tok left)`)\n)\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor node, m := range code.Matches(pass, checkYodaConditionsQ) {\n\t\tedits := code.EditMatch(pass, node, m, checkYodaConditionsR)\n\t\treport.Report(pass, node, \"don't use Yoda conditions\",\n\t\t\treport.FilterGenerated(),\n\t\t\treport.Fixes(edit.Fix(\"Un-Yoda-fy\", edits...)))\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "stylecheck/st1017/st1017_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1017\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1017/testdata/go1.0/CheckYodaConditions/CheckYodaConditions.go",
    "content": "// Package pkg ...\npackage pkg\n\nfunc fn(x string, y int) {\n\tif \"\" == x { //@ diag(`Yoda`)\n\t}\n\tif 0 == y { //@ diag(`Yoda`)\n\t}\n\tif 0 > y {\n\t}\n\tif \"\" == \"\" {\n\t}\n\n\tif \"\" == \"\" || 0 == y { //@ diag(`Yoda`)\n\t}\n}\n"
  },
  {
    "path": "stylecheck/st1017/testdata/go1.0/CheckYodaConditions/CheckYodaConditions.go.golden",
    "content": "// Package pkg ...\npackage pkg\n\nfunc fn(x string, y int) {\n\tif x == \"\" { //@ diag(`Yoda`)\n\t}\n\tif y == 0 { //@ diag(`Yoda`)\n\t}\n\tif 0 > y {\n\t}\n\tif \"\" == \"\" {\n\t}\n\n\tif \"\" == \"\" || y == 0 { //@ diag(`Yoda`)\n\t}\n}\n"
  },
  {
    "path": "stylecheck/st1018/st1018.go",
    "content": "package st1018\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"strconv\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1018\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:   `Avoid zero-width and control characters in string literals`,\n\t\tSince:   \"2019.2\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tlit := node.(*ast.BasicLit)\n\t\tif lit.Kind != token.STRING {\n\t\t\treturn\n\t\t}\n\n\t\ttype invalid struct {\n\t\t\tr   rune\n\t\t\toff int\n\t\t}\n\t\tvar invalids []invalid\n\t\thasFormat := false\n\t\thasControl := false\n\t\tprev := rune(-1)\n\t\tconst zwj = '\\u200d'\n\t\tfor off, r := range lit.Value {\n\t\t\tif unicode.Is(unicode.Cf, r) {\n\t\t\t\tif r >= '\\U000e0020' && r <= '\\U000e007f' {\n\t\t\t\t\t// These are used for spelling out country codes for flag emoji\n\t\t\t\t} else if unicode.Is(unicode.Variation_Selector, r) {\n\t\t\t\t\t// Always allow variation selectors\n\t\t\t\t} else if r == zwj && (unicode.Is(unicode.S, prev) || unicode.Is(unicode.Variation_Selector, prev)) {\n\t\t\t\t\t// Allow zero-width joiner in emoji, including those that use variation selectors.\n\n\t\t\t\t\t// Technically some foreign scripts make valid use of zero-width joiners, too, but for now we'll err\n\t\t\t\t\t// on the side of flagging all non-emoji uses of ZWJ.\n\t\t\t\t} else {\n\t\t\t\t\tswitch r {\n\t\t\t\t\tcase '\\u0600', '\\u0601', '\\u0602', '\\u0603', '\\u0604', '\\u0605', '\\u0890', '\\u0891', '\\u08e2':\n\t\t\t\t\t\t// Arabic characters that are not actually invisible. If anyone knows why these are in the\n\t\t\t\t\t\t// Other, Format category please let me know.\n\t\t\t\t\tcase '\\u061c', '\\u202A', '\\u202B', '\\u202D', '\\u202E', '\\u2066', '\\u2067', '\\u2068', '\\u202C', '\\u2069':\n\t\t\t\t\t\t// Bidirectional formatting characters. At best they will render confusingly, at worst they're used\n\t\t\t\t\t\t// to cause confusion.\n\t\t\t\t\t\tfallthrough\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tinvalids = append(invalids, invalid{r, off})\n\t\t\t\t\t\thasFormat = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if unicode.Is(unicode.Cc, r) && r != '\\n' && r != '\\t' && r != '\\r' {\n\t\t\t\tinvalids = append(invalids, invalid{r, off})\n\t\t\t\thasControl = true\n\t\t\t}\n\t\t\tprev = r\n\t\t}\n\n\t\tswitch len(invalids) {\n\t\tcase 0:\n\t\t\treturn\n\t\tcase 1:\n\t\t\tvar kind string\n\t\t\tif hasFormat {\n\t\t\t\tkind = \"format\"\n\t\t\t} else if hasControl {\n\t\t\t\tkind = \"control\"\n\t\t\t} else {\n\t\t\t\tpanic(\"unreachable\")\n\t\t\t}\n\n\t\t\tr := invalids[0]\n\t\t\tmsg := fmt.Sprintf(\"string literal contains the Unicode %s character %U, consider using the %q escape sequence instead\", kind, r.r, r.r)\n\n\t\t\treplacement := strconv.QuoteRune(r.r)\n\t\t\treplacement = replacement[1 : len(replacement)-1]\n\t\t\tedit := analysis.SuggestedFix{\n\t\t\t\tMessage: fmt.Sprintf(\"replace %s character %U with %q\", kind, r.r, r.r),\n\t\t\t\tTextEdits: []analysis.TextEdit{{\n\t\t\t\t\tPos:     lit.Pos() + token.Pos(r.off),\n\t\t\t\t\tEnd:     lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)),\n\t\t\t\t\tNewText: []byte(replacement),\n\t\t\t\t}},\n\t\t\t}\n\t\t\tdelete := analysis.SuggestedFix{\n\t\t\t\tMessage: fmt.Sprintf(\"delete %s character %U\", kind, r.r),\n\t\t\t\tTextEdits: []analysis.TextEdit{{\n\t\t\t\t\tPos: lit.Pos() + token.Pos(r.off),\n\t\t\t\t\tEnd: lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)),\n\t\t\t\t}},\n\t\t\t}\n\t\t\treport.Report(pass, lit, msg, report.Fixes(edit, delete))\n\t\tdefault:\n\t\t\tvar kind string\n\t\t\tif hasFormat && hasControl {\n\t\t\t\tkind = \"format and control\"\n\t\t\t} else if hasFormat {\n\t\t\t\tkind = \"format\"\n\t\t\t} else if hasControl {\n\t\t\t\tkind = \"control\"\n\t\t\t} else {\n\t\t\t\tpanic(\"unreachable\")\n\t\t\t}\n\n\t\t\tmsg := fmt.Sprintf(\"string literal contains Unicode %s characters, consider using escape sequences instead\", kind)\n\t\t\tvar edits []analysis.TextEdit\n\t\t\tvar deletions []analysis.TextEdit\n\t\t\tfor _, r := range invalids {\n\t\t\t\treplacement := strconv.QuoteRune(r.r)\n\t\t\t\treplacement = replacement[1 : len(replacement)-1]\n\t\t\t\tedits = append(edits, analysis.TextEdit{\n\t\t\t\t\tPos:     lit.Pos() + token.Pos(r.off),\n\t\t\t\t\tEnd:     lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)),\n\t\t\t\t\tNewText: []byte(replacement),\n\t\t\t\t})\n\t\t\t\tdeletions = append(deletions, analysis.TextEdit{\n\t\t\t\t\tPos: lit.Pos() + token.Pos(r.off),\n\t\t\t\t\tEnd: lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)),\n\t\t\t\t})\n\t\t\t}\n\t\t\tedit := analysis.SuggestedFix{\n\t\t\t\tMessage:   fmt.Sprintf(\"replace all %s characters with escape sequences\", kind),\n\t\t\t\tTextEdits: edits,\n\t\t\t}\n\t\t\tdelete := analysis.SuggestedFix{\n\t\t\t\tMessage:   fmt.Sprintf(\"delete all %s characters\", kind),\n\t\t\t\tTextEdits: deletions,\n\t\t\t}\n\t\t\treport.Report(pass, lit, msg, report.Fixes(edit, delete))\n\t\t}\n\t}\n\tcode.Preorder(pass, fn, (*ast.BasicLit)(nil))\n\treturn nil, nil\n}\n"
  },
  {
    "path": "stylecheck/st1018/st1018_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1018\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1018/testdata/go1.0/CheckInvisibleCharacters/CheckInvisibleCharacters.go",
    "content": "// Package pkg ...\npackage pkg\n\nvar (\n\ta = \"\u0007\"  //@ diag(`Unicode control character U+0007`)\n\tb = \"\u0007\u001a\" //@ diag(`Unicode control characters`)\n\tc = \"Test\ttest\"\n\td = `T\nest`\n\te = `Zero​Width` //@ diag(`Unicode format character U+200B`)\n\tf = \"\\u200b\"\n\tg = \"👩🏽‍🔬\u0007\" //@ diag(`Unicode control character U+0007`)\n\th = \"👩🏽‍🔬\u0007​\" //@ diag(`Unicode format and control characters`)\n\ti = \"👨‍👩‍👦\"\n\tj = \"🏳️‍🌈\"\n\tk = \"🏴󠁧󠁢󠁷󠁬󠁳󠁿\"\n)\n"
  },
  {
    "path": "stylecheck/st1018/testdata/go1.0/CheckInvisibleCharacters/CheckInvisibleCharacters.go.golden",
    "content": "-- replace control character U+0007 with '\\a' --\n// Package pkg ...\npackage pkg\n\nvar (\n\ta = \"\\a\" //@ diag(`Unicode control character U+0007`)\n\tb = \"\u0007\u001a\" //@ diag(`Unicode control characters`)\n\tc = \"Test\ttest\"\n\td = `T\nest`\n\te = `Zero​Width` //@ diag(`Unicode format character U+200B`)\n\tf = \"\\u200b\"\n\tg = \"👩🏽‍🔬\\a\" //@ diag(`Unicode control character U+0007`)\n\th = \"👩🏽‍🔬\u0007​\" //@ diag(`Unicode format and control characters`)\n\ti = \"👨‍👩‍👦\"\n\tj = \"🏳️‍🌈\"\n\tk = \"🏴󠁧󠁢󠁷󠁬󠁳󠁿\"\n)\n\n-- delete control character U+0007 --\n// Package pkg ...\npackage pkg\n\nvar (\n\ta = \"\"   //@ diag(`Unicode control character U+0007`)\n\tb = \"\u0007\u001a\" //@ diag(`Unicode control characters`)\n\tc = \"Test\ttest\"\n\td = `T\nest`\n\te = `Zero​Width` //@ diag(`Unicode format character U+200B`)\n\tf = \"\\u200b\"\n\tg = \"👩🏽‍🔬\"   //@ diag(`Unicode control character U+0007`)\n\th = \"👩🏽‍🔬\u0007​\" //@ diag(`Unicode format and control characters`)\n\ti = \"👨‍👩‍👦\"\n\tj = \"🏳️‍🌈\"\n\tk = \"🏴󠁧󠁢󠁷󠁬󠁳󠁿\"\n)\n\n-- delete format character U+200B --\n// Package pkg ...\npackage pkg\n\nvar (\n\ta = \"\u0007\"  //@ diag(`Unicode control character U+0007`)\n\tb = \"\u0007\u001a\" //@ diag(`Unicode control characters`)\n\tc = \"Test\ttest\"\n\td = `T\nest`\n\te = `ZeroWidth` //@ diag(`Unicode format character U+200B`)\n\tf = \"\\u200b\"\n\tg = \"👩🏽‍🔬\u0007\"  //@ diag(`Unicode control character U+0007`)\n\th = \"👩🏽‍🔬\u0007​\" //@ diag(`Unicode format and control characters`)\n\ti = \"👨‍👩‍👦\"\n\tj = \"🏳️‍🌈\"\n\tk = \"🏴󠁧󠁢󠁷󠁬󠁳󠁿\"\n)\n\n-- replace format character U+200B with '\\u200b' --\n// Package pkg ...\npackage pkg\n\nvar (\n\ta = \"\u0007\"  //@ diag(`Unicode control character U+0007`)\n\tb = \"\u0007\u001a\" //@ diag(`Unicode control characters`)\n\tc = \"Test\ttest\"\n\td = `T\nest`\n\te = `Zero\\u200bWidth` //@ diag(`Unicode format character U+200B`)\n\tf = \"\\u200b\"\n\tg = \"👩🏽‍🔬\u0007\"  //@ diag(`Unicode control character U+0007`)\n\th = \"👩🏽‍🔬\u0007​\" //@ diag(`Unicode format and control characters`)\n\ti = \"👨‍👩‍👦\"\n\tj = \"🏳️‍🌈\"\n\tk = \"🏴󠁧󠁢󠁷󠁬󠁳󠁿\"\n)\n\n-- delete all control characters --\n// Package pkg ...\npackage pkg\n\nvar (\n\ta = \"\u0007\" //@ diag(`Unicode control character U+0007`)\n\tb = \"\"  //@ diag(`Unicode control characters`)\n\tc = \"Test\ttest\"\n\td = `T\nest`\n\te = `Zero​Width` //@ diag(`Unicode format character U+200B`)\n\tf = \"\\u200b\"\n\tg = \"👩🏽‍🔬\u0007\"  //@ diag(`Unicode control character U+0007`)\n\th = \"👩🏽‍🔬\u0007​\" //@ diag(`Unicode format and control characters`)\n\ti = \"👨‍👩‍👦\"\n\tj = \"🏳️‍🌈\"\n\tk = \"🏴󠁧󠁢󠁷󠁬󠁳󠁿\"\n)\n\n-- replace all control characters with escape sequences --\n// Package pkg ...\npackage pkg\n\nvar (\n\ta = \"\u0007\"      //@ diag(`Unicode control character U+0007`)\n\tb = \"\\a\\x1a\" //@ diag(`Unicode control characters`)\n\tc = \"Test\ttest\"\n\td = `T\nest`\n\te = `Zero​Width` //@ diag(`Unicode format character U+200B`)\n\tf = \"\\u200b\"\n\tg = \"👩🏽‍🔬\u0007\"  //@ diag(`Unicode control character U+0007`)\n\th = \"👩🏽‍🔬\u0007​\" //@ diag(`Unicode format and control characters`)\n\ti = \"👨‍👩‍👦\"\n\tj = \"🏳️‍🌈\"\n\tk = \"🏴󠁧󠁢󠁷󠁬󠁳󠁿\"\n)\n\n\n-- delete all format and control characters --\n// Package pkg ...\npackage pkg\n\nvar (\n\ta = \"\u0007\"  //@ diag(`Unicode control character U+0007`)\n\tb = \"\u0007\u001a\" //@ diag(`Unicode control characters`)\n\tc = \"Test\ttest\"\n\td = `T\nest`\n\te = `Zero​Width` //@ diag(`Unicode format character U+200B`)\n\tf = \"\\u200b\"\n\tg = \"👩🏽‍🔬\u0007\" //@ diag(`Unicode control character U+0007`)\n\th = \"👩🏽‍🔬\"  //@ diag(`Unicode format and control characters`)\n\ti = \"👨‍👩‍👦\"\n\tj = \"🏳️‍🌈\"\n\tk = \"🏴󠁧󠁢󠁷󠁬󠁳󠁿\"\n)\n\n-- replace all format and control characters with escape sequences --\n// Package pkg ...\npackage pkg\n\nvar (\n\ta = \"\u0007\"  //@ diag(`Unicode control character U+0007`)\n\tb = \"\u0007\u001a\" //@ diag(`Unicode control characters`)\n\tc = \"Test\ttest\"\n\td = `T\nest`\n\te = `Zero​Width` //@ diag(`Unicode format character U+200B`)\n\tf = \"\\u200b\"\n\tg = \"👩🏽‍🔬\u0007\"        //@ diag(`Unicode control character U+0007`)\n\th = \"👩🏽‍🔬\\a\\u200b\" //@ diag(`Unicode format and control characters`)\n\ti = \"👨‍👩‍👦\"\n\tj = \"🏳️‍🌈\"\n\tk = \"🏴󠁧󠁢󠁷󠁬󠁳󠁿\"\n)\n"
  },
  {
    "path": "stylecheck/st1019/st1019.go",
    "content": "package st1019\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1019\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{generated.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: `Importing the same package multiple times`,\n\t\tText: `Go allows importing the same package multiple times, as long as\ndifferent import aliases are being used. That is, the following\nbit of code is valid:\n\n    import (\n        \"fmt\"\n        fumpt \"fmt\"\n        format \"fmt\"\n    )\n\nHowever, this is very rarely done on purpose. Usually, it is a\nsign of code that got refactored, accidentally adding duplicate\nimport statements. It is also a rarely known feature, which may\ncontribute to confusion.\n\nDo note that sometimes, this feature may be used\nintentionally (see for example\nhttps://github.com/golang/go/commit/3409ce39bfd7584523b7a8c150a310cea92d879d)\n– if you want to allow this pattern in your code base, you're\nadvised to disable this check.\n\nIt is acceptable to import the same package twice if one of the imports\nuses the blank identifier. This is allowed in order to increase\nresilience against erroneous changes when using the same package for its\nside effects as well as its exported API.`,\n\t\tSince:   \"2020.1\",\n\t\tMergeIf: lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfor _, f := range pass.Files {\n\t\t// Collect all imports by their import path\n\t\timports := make(map[string][]*ast.ImportSpec, len(f.Imports))\n\t\tfor _, imp := range f.Imports {\n\t\t\tif imp.Name != nil && imp.Name.Name == \"_\" {\n\t\t\t\t// Allow blank imports to coexist with one normal import.\n\t\t\t\t//\n\t\t\t\t// We don't have to count the number of blank imports,\n\t\t\t\t// goimports removes duplicates.\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\timports[imp.Path.Value] = append(imports[imp.Path.Value], imp)\n\t\t}\n\n\t\tfor path, value := range imports {\n\t\t\tif path[1:len(path)-1] == \"unsafe\" {\n\t\t\t\t// Don't flag unsafe. Cgo generated code imports\n\t\t\t\t// unsafe as _cgo_unsafe, in addition to the user's import.\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// If there's more than one import per path, we flag that\n\t\t\tif len(value) > 1 {\n\t\t\t\ts := fmt.Sprintf(\"package %s is being imported more than once\", path)\n\t\t\t\topts := []report.Option{report.FilterGenerated()}\n\t\t\t\tfor _, imp := range value[1:] {\n\t\t\t\t\topts = append(opts, report.Related(imp, fmt.Sprintf(\"other import of %s\", path)))\n\t\t\t\t}\n\t\t\t\treport.Report(pass, value[0], s, opts...)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "stylecheck/st1019/st1019_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1019\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1019/testdata/go1.0/CheckDuplicatedImports/CheckDuplicatedImports.go",
    "content": "// Package pkg ...\npackage pkg\n\nimport (\n\t\"fmt\" //@ diag(`package \"fmt\" is being imported more than once`)\n\tfmt1 \"fmt\"\n\tfmt2 \"fmt\"\n\n\tfine \"net/http\"\n\n\t\"os\" //@ diag(`package \"os\" is being imported more than once`)\n\tos1 \"os\"\n\n\t\"C\"\n\t_ \"unsafe\"\n\n\t// This imports the package for its side effects, and then again to use it.\n\t// If we flagged this, the user would have to remove the _ import. But if\n\t// later the user stopped using the package directly, they'd be prone to\n\t// removing the import, losing its side effects.\n\t\"net/http/pprof\"\n\t_ \"net/http/pprof\"\n\n\t_ \"strconv\"\n\tstrconv1 \"strconv\" //@ diag(`package \"strconv\" is being imported more than once`)\n\tstrconv2 \"strconv\"\n)\n\nvar _ = fmt.Println\nvar _ = fmt1.Println\nvar _ = fmt2.Println\nvar _ = fine.ListenAndServe\nvar _ = os.Getenv\nvar _ = os1.Getenv\nvar _ = pprof.Cmdline\nvar _ = strconv1.AppendBool\nvar _ = strconv2.AppendBool\n"
  },
  {
    "path": "stylecheck/st1020/st1020.go",
    "content": "package st1020\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1020\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{generated.Analyzer, inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"The documentation of an exported function should start with the function's name\",\n\t\tText: `Doc comments work best as complete sentences, which\nallow a wide variety of automated presentations. The first sentence\nshould be a one-sentence summary that starts with the name being\ndeclared.\n\nIf every doc comment begins with the name of the item it describes,\nyou can use the \\'doc\\' subcommand of the \\'go\\' tool and run the output\nthrough grep.\n\nSee https://go.dev/doc/effective_go#commentary for more\ninformation on how to write good documentation.`,\n\t\tSince:      \"2020.1\",\n\t\tNonDefault: true,\n\t\tMergeIf:    lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tfn := func(node ast.Node) {\n\t\tif code.IsInTest(pass, node) {\n\t\t\treturn\n\t\t}\n\n\t\tdecl := node.(*ast.FuncDecl)\n\t\ttext, ok := docText(decl.Doc)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\tif !ast.IsExported(decl.Name.Name) {\n\t\t\treturn\n\t\t}\n\t\tif strings.HasPrefix(text, \"Deprecated: \") {\n\t\t\treturn\n\t\t}\n\n\t\tkind := \"function\"\n\t\tif decl.Recv != nil {\n\t\t\tkind = \"method\"\n\t\t\tvar ident *ast.Ident\n\t\t\tT := decl.Recv.List[0].Type\n\t\t\tif T_, ok := T.(*ast.StarExpr); ok {\n\t\t\t\tT = T_.X\n\t\t\t}\n\t\t\tswitch T := T.(type) {\n\t\t\tcase *ast.IndexExpr:\n\t\t\t\tident = T.X.(*ast.Ident)\n\t\t\tcase *ast.IndexListExpr:\n\t\t\t\tident = T.X.(*ast.Ident)\n\t\t\tcase *ast.Ident:\n\t\t\t\tident = T\n\t\t\tdefault:\n\t\t\t\tlint.ExhaustiveTypeSwitch(T)\n\t\t\t}\n\t\t\tif !ast.IsExported(ident.Name) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tprefix := decl.Name.Name + \" \"\n\t\tif !strings.HasPrefix(text, prefix) {\n\t\t\treport.Report(pass, decl.Doc, fmt.Sprintf(`comment on exported %s %s should be of the form \"%s...\"`, kind, decl.Name.Name, prefix), report.FilterGenerated())\n\t\t}\n\t}\n\n\tcode.Preorder(pass, fn, (*ast.FuncDecl)(nil))\n\treturn nil, nil\n}\n\nfunc docText(doc *ast.CommentGroup) (string, bool) {\n\tif doc == nil {\n\t\treturn \"\", false\n\t}\n\t// We trim spaces primarily because of /**/ style comments, which often have leading space.\n\ttext := strings.TrimSpace(doc.Text())\n\treturn text, text != \"\"\n}\n"
  },
  {
    "path": "stylecheck/st1020/st1020_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1020\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1020/testdata/go1.0/CheckExportedFunctionDocs/CheckExportedFunctionDocs.go",
    "content": "package pkg\n\n// whatever\nfunc foo() {}\n\n// Foo is amazing\nfunc Foo() {}\n\n// Whatever //@ diag(`comment on exported function`)\nfunc Bar() {}\n\ntype T struct{}\n\n// Whatever\nfunc (T) foo() {}\n\n// Foo is amazing\nfunc (T) Foo() {}\n\n// Whatever //@ diag(`comment on exported method`)\nfunc (T) Bar() {}\n\n// Deprecated: don't use.\nfunc (T) Dep() {}\n\n//\nfunc Qux() {} // this is fine, because \"no comment\" and \"empty comment\" are treated the same\n\n//\n// Meow is amazing.\n//\n// godoc allows this style, because ast.CommentGroup.Text strips whitespace.\n// We currently make no effort to flag it.\n//\nfunc Meow() {}\n\n//some:directive\nfunc F1() {} // we pretend that directives aren't part of the doc string, just like godoc in Go 1.15+ does\n\n//some:directive\n// F2 is amazing\nfunc F2() {}\n\n//some:directive //@ diag(`comment on exported function`)\n// Whatever\nfunc F3() {}\n\n// Deprecated: don't use.\nfunc F4() {}\n\n//some:directive\n// Deprecated: don't use.\nfunc F5() {}\n\n// wrong comment yo. //@diag (`comment on exported function`)\n//\n// Deprecated: don't use.\nfunc F6() {}\n"
  },
  {
    "path": "stylecheck/st1020/testdata/go1.0/CheckExportedFunctionDocs/foo_test.go",
    "content": "package pkg\n\nimport \"testing\"\n\n// This is a test\nfunc TestFoo(t *testing.T) {}\n"
  },
  {
    "path": "stylecheck/st1020/testdata/go1.18/CheckExportedFunctionDocs/generics.go",
    "content": "package pkg\n\n// Whatever //@ diag(`comment on exported function`)\nfunc TPFoo[T any]() {}\n\n// Whatever //@ diag(`comment on exported function`)\nfunc TPBar[T1, T2 any]() {}\n\n// TPBaz is amazing\nfunc TPBaz[T any]() {}\n\ntype TPT[T any] struct{}\n\n// Foo is amazing\nfunc (TPT[T]) Foo() {}\n\n// Whatever //@ diag(`comment on exported method`)\nfunc (TPT[T]) Bar() {}\n\ntype TPT2[T1, T2 any] struct{}\n\n// Foo is amazing\nfunc (TPT2[T1, T2]) Foo() {}\n\n// Whatever //@ diag(`comment on exported method`)\nfunc (*TPT2[T1, T2]) Bar() {}\n\n// Deprecated: don't use.\nfunc TPFooDepr[T any]() {}\n\n// Deprecated: don't use.\nfunc TPBarDepr[T1, T2 any]() {}\n\n// Deprecated: don't use.\nfunc (TPT[T]) BarDepr() {}\n\n// Deprecated: don't use.\nfunc (*TPT2[T1, T2]) BarDepr() {}\n"
  },
  {
    "path": "stylecheck/st1021/st1021.go",
    "content": "package st1021\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1021\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{generated.Analyzer, inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"The documentation of an exported type should start with type's name\",\n\t\tText: `Doc comments work best as complete sentences, which\nallow a wide variety of automated presentations. The first sentence\nshould be a one-sentence summary that starts with the name being\ndeclared.\n\nIf every doc comment begins with the name of the item it describes,\nyou can use the \\'doc\\' subcommand of the \\'go\\' tool and run the output\nthrough grep.\n\nSee https://go.dev/doc/effective_go#commentary for more\ninformation on how to write good documentation.`,\n\t\tSince:      \"2020.1\",\n\t\tNonDefault: true,\n\t\tMergeIf:    lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tvar genDecl *ast.GenDecl\n\tfn := func(node ast.Node, push bool) bool {\n\t\tif !push {\n\t\t\tgenDecl = nil\n\t\t\treturn false\n\t\t}\n\t\tif code.IsInTest(pass, node) {\n\t\t\treturn false\n\t\t}\n\n\t\tswitch node := node.(type) {\n\t\tcase *ast.GenDecl:\n\t\t\tif node.Tok == token.IMPORT {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tgenDecl = node\n\t\t\treturn true\n\t\tcase *ast.TypeSpec:\n\t\t\tif !ast.IsExported(node.Name.Name) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tdoc := node.Doc\n\t\t\ttext, ok := docText(doc)\n\t\t\tif !ok {\n\t\t\t\tif len(genDecl.Specs) != 1 {\n\t\t\t\t\t// more than one spec in the GenDecl, don't validate the\n\t\t\t\t\t// docstring\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif genDecl.Lparen.IsValid() {\n\t\t\t\t\t// 'type ( T )' is weird, don't guess the user's intention\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tdoc = genDecl.Doc\n\t\t\t\ttext, ok = docText(doc)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check comment before we strip articles in case the type's name is an article.\n\t\t\tif strings.HasPrefix(text, node.Name.Name+\" \") {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\ts := text\n\t\t\tarticles := [...]string{\"A\", \"An\", \"The\"}\n\t\t\tfor _, a := range articles {\n\t\t\t\tif strings.HasPrefix(s, a+\" \") {\n\t\t\t\t\ts = s[len(a)+1:]\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !strings.HasPrefix(s, node.Name.Name+\" \") {\n\t\t\t\treport.Report(pass, doc, fmt.Sprintf(`comment on exported type %s should be of the form \"%s ...\" (with optional leading article)`, node.Name.Name, node.Name.Name), report.FilterGenerated())\n\t\t\t}\n\t\t\treturn false\n\t\tcase *ast.FuncLit, *ast.FuncDecl:\n\t\t\treturn false\n\t\tdefault:\n\t\t\tlint.ExhaustiveTypeSwitch(node)\n\t\t\treturn false\n\t\t}\n\t}\n\n\tpass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.GenDecl)(nil), (*ast.TypeSpec)(nil), (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil)}, fn)\n\treturn nil, nil\n}\n\nfunc docText(doc *ast.CommentGroup) (string, bool) {\n\tif doc == nil {\n\t\treturn \"\", false\n\t}\n\t// We trim spaces primarily because of /**/ style comments, which often have leading space.\n\ttext := strings.TrimSpace(doc.Text())\n\treturn text, text != \"\"\n}\n"
  },
  {
    "path": "stylecheck/st1021/st1021_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1021\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1021/testdata/go1.0/CheckExportedTypeDocs/CheckExportedTypeDocs.go",
    "content": "package pkg\n\n// Some type\ntype t1 struct{}\n\n// Some type //@ diag(`comment on exported type`)\ntype T2 struct{}\n\n// T3 is amazing\ntype T3 struct{}\n\ntype (\n\t// Some type //@ diag(`comment on exported type`)\n\tT4 struct{}\n\t// The T5 type is amazing\n\tT5 struct{}\n\t// Some type\n\tt6 struct{}\n)\n\n// Some types\ntype (\n\tT7 struct{}\n\tT8 struct{}\n)\n\n// Some types\ntype (\n\tT9 struct{}\n)\n\nfunc fn() {\n\t// Some type\n\ttype T1 struct{}\n}\n\n//\ntype T10 struct{} // this is fine, because \"no comment\" and \"empty comment\" are treated the same\n\n//\n// T11 is amazing.\n//\n// godoc allows this style, because ast.CommentGroup.Text strips whitespace.\n// We currently make no effort to flag it.\n//\ntype T11 struct{}\n\n//some:directive\ntype T12 struct{} // we pretend that directives aren't part of the doc string, just like godoc in Go 1.15+ does\n\n//some:directive\n// T13 is amazing\ntype T13 struct{}\n\n//some:directive //@ diag(`comment on exported type`)\n// Whatever\ntype T14 struct{}\n\n// A does stuff.\ntype A struct{}\n"
  },
  {
    "path": "stylecheck/st1022/st1022.go",
    "content": "package st1022\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/code\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/analysis/passes/inspect\"\n\t\"golang.org/x/tools/go/ast/inspector\"\n)\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:     \"ST1022\",\n\t\tRun:      run,\n\t\tRequires: []*analysis.Analyzer{generated.Analyzer, inspect.Analyzer},\n\t},\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"The documentation of an exported variable or constant should start with variable's name\",\n\t\tText: `Doc comments work best as complete sentences, which\nallow a wide variety of automated presentations. The first sentence\nshould be a one-sentence summary that starts with the name being\ndeclared.\n\nIf every doc comment begins with the name of the item it describes,\nyou can use the \\'doc\\' subcommand of the \\'go\\' tool and run the output\nthrough grep.\n\nSee https://go.dev/doc/effective_go#commentary for more\ninformation on how to write good documentation.`,\n\t\tSince:      \"2020.1\",\n\t\tNonDefault: true,\n\t\tMergeIf:    lint.MergeIfAny,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tvar genDecl *ast.GenDecl\n\tfn := func(node ast.Node, push bool) bool {\n\t\tif !push {\n\t\t\tgenDecl = nil\n\t\t\treturn false\n\t\t}\n\t\tif code.IsInTest(pass, node) {\n\t\t\treturn false\n\t\t}\n\n\t\tswitch node := node.(type) {\n\t\tcase *ast.GenDecl:\n\t\t\tif node.Tok == token.IMPORT {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tgenDecl = node\n\t\t\treturn true\n\t\tcase *ast.ValueSpec:\n\t\t\tif genDecl.Lparen.IsValid() || len(node.Names) > 1 {\n\t\t\t\t// Don't try to guess the user's intention\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tname := node.Names[0].Name\n\t\t\tif !ast.IsExported(name) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\ttext, ok := docText(genDecl.Doc)\n\t\t\tif !ok {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tprefix := name + \" \"\n\t\t\tif !strings.HasPrefix(text, prefix) {\n\t\t\t\tkind := \"var\"\n\t\t\t\tif genDecl.Tok == token.CONST {\n\t\t\t\t\tkind = \"const\"\n\t\t\t\t}\n\t\t\t\treport.Report(pass, genDecl.Doc, fmt.Sprintf(`comment on exported %s %s should be of the form \"%s...\"`, kind, name, prefix), report.FilterGenerated())\n\t\t\t}\n\t\t\treturn false\n\t\tcase *ast.FuncLit, *ast.FuncDecl:\n\t\t\treturn false\n\t\tdefault:\n\t\t\tlint.ExhaustiveTypeSwitch(node)\n\t\t\treturn false\n\t\t}\n\t}\n\n\tpass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.GenDecl)(nil), (*ast.ValueSpec)(nil), (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil)}, fn)\n\treturn nil, nil\n}\n\nfunc docText(doc *ast.CommentGroup) (string, bool) {\n\tif doc == nil {\n\t\treturn \"\", false\n\t}\n\t// We trim spaces primarily because of /**/ style comments, which often have leading space.\n\ttext := strings.TrimSpace(doc.Text())\n\treturn text, text != \"\"\n}\n"
  },
  {
    "path": "stylecheck/st1022/st1022_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1022\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1022/testdata/go1.0/CheckExportedVarDocs/CheckExportedVarDocs.go",
    "content": "package pkg\n\n// Whatever\nvar a int\n\n// Whatever //@ diag(`should be of the form`)\nvar B int\n\n// Whatever\nvar (\n\t// Whatever\n\tC int\n)\n\nfunc fn() {\n\t// Whatever\n\tvar D int\n\t_ = D\n}\n\n//\nvar E int // this is fine, because \"no comment\" and \"empty comment\" are treated the same\n\n//\n// F is amazing.\n//\n// godoc allows this style, because ast.CommentGroup.Text strips whitespace.\n// We currently make no effort to flag it.\n//\nvar F int\n\n//some:directive\nvar G int // we pretend that directives aren't part of the doc string, just like godoc in Go 1.15+ does\n\n//some:directive\n// H is amazing\nvar H int\n\n//some:directive //@ diag(`should be of the form`)\n// Whatever\nvar I int\n"
  },
  {
    "path": "stylecheck/st1023/st1023.go",
    "content": "package st1023\n\nimport (\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/internal/sharedcheck\"\n)\n\nfunc init() {\n\tSCAnalyzer.Analyzer.Name = \"ST1023\"\n}\n\nvar SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{\n\tAnalyzer: sharedcheck.RedundantTypeInDeclarationChecker(\"should\", false),\n\tDoc: &lint.RawDocumentation{\n\t\tTitle:      \"Redundant type in variable declaration\",\n\t\tSince:      \"2021.1\",\n\t\tNonDefault: true,\n\t\tMergeIf:    lint.MergeIfAll,\n\t},\n})\n\nvar Analyzer = SCAnalyzer.Analyzer\n"
  },
  {
    "path": "stylecheck/st1023/st1023_test.go",
    "content": "// Code generated by generate.go. DO NOT EDIT.\n\npackage st1023\n\nimport (\n\t\"testing\"\n\n\t\"honnef.co/go/tools/analysis/lint/testutil\"\n)\n\nfunc TestTestdata(t *testing.T) {\n\ttestutil.Run(t, SCAnalyzer)\n}\n"
  },
  {
    "path": "stylecheck/st1023/testdata/go1.0/CheckRedundantTypeInDeclaration/CheckRedundantTypeInDeclaration.go",
    "content": "package pkg\n\nimport (\n\t\"io\"\n\t\"math\"\n)\n\ntype MyInt int\n\nconst X int = 1\nconst Y = 1\n\nfunc gen1() int           { return 0 }\nfunc gen2() io.ReadCloser { return nil }\nfunc gen3() MyInt         { return 0 }\n\n// don't flag global variables\nvar a int = gen1()\n\nfunc fn() {\n\tvar _ int = gen1()           // don't flag blank identifier\n\tvar a int = Y                // don't flag named untyped constants\n\tvar b int = 1                //@ diag(`should omit type int`)\n\tvar c int = 1.0              // different default type\n\tvar d MyInt = 1              // different default type\n\tvar e io.ReadCloser = gen2() //@ diag(`should omit type io.ReadCloser`)\n\tvar f io.Reader = gen2()     // different interface type\n\tvar g float64 = math.Pi      // don't flag named untyped constants\n\tvar h bool = true            //@ diag(`should omit type bool`)\n\tvar i string = \"\"            //@ diag(`should omit type string`)\n\tvar j MyInt = gen3()         //@ diag(`should omit type MyInt`)\n\tvar k uint8 = Y              // different default type on constant\n\tvar l uint8 = (Y + Y) / 2    // different default type on rhs\n\tvar m int = (Y + Y) / 2      // complex expression\n\n\t_, _, _, _, _, _, _, _, _, _, _, _, _ = a, b, c, d, e, f, g, h, i, j, k, l, m\n}\n"
  },
  {
    "path": "stylecheck/st1023/testdata/go1.0/CheckRedundantTypeInDeclaration/CheckRedundantTypeInDeclaration.go.golden",
    "content": "package pkg\n\nimport (\n\t\"io\"\n\t\"math\"\n)\n\ntype MyInt int\n\nconst X int = 1\nconst Y = 1\n\nfunc gen1() int           { return 0 }\nfunc gen2() io.ReadCloser { return nil }\nfunc gen3() MyInt         { return 0 }\n\n// don't flag global variables\nvar a int = gen1()\n\nfunc fn() {\n\tvar _ int = gen1()        // don't flag blank identifier\n\tvar a int = Y             // don't flag named untyped constants\n\tvar b = 1                 //@ diag(`should omit type int`)\n\tvar c int = 1.0           // different default type\n\tvar d MyInt = 1           // different default type\n\tvar e = gen2()            //@ diag(`should omit type io.ReadCloser`)\n\tvar f io.Reader = gen2()  // different interface type\n\tvar g float64 = math.Pi   // don't flag named untyped constants\n\tvar h = true              //@ diag(`should omit type bool`)\n\tvar i = \"\"                //@ diag(`should omit type string`)\n\tvar j = gen3()            //@ diag(`should omit type MyInt`)\n\tvar k uint8 = Y           // different default type on constant\n\tvar l uint8 = (Y + Y) / 2 // different default type on rhs\n\tvar m int = (Y + Y) / 2   // complex expression\n\n\t_, _, _, _, _, _, _, _, _, _, _, _, _ = a, b, c, d, e, f, g, h, i, j, k, l, m\n}\n"
  },
  {
    "path": "stylecheck/st1023/testdata/go1.0/CheckRedundantTypeInDeclaration_syscall/CheckRedundantTypeInDeclaration_syscall.go",
    "content": "package pkg\n\nimport _ \"syscall\"\n\nfunc fn() {\n\t// not flagged because we're importing syscall\n\tvar x int = 1\n\t_ = x\n}\n"
  },
  {
    "path": "unused/implements.go",
    "content": "package unused\n\nimport (\n\t\"go/types\"\n)\n\n// lookupMethod returns the index of and method with matching package and name, or (-1, nil).\nfunc lookupMethod(T *types.Interface, pkg *types.Package, name string) (int, *types.Func) {\n\tif name != \"_\" {\n\t\tfor i := 0; i < T.NumMethods(); i++ {\n\t\t\tm := T.Method(i)\n\t\t\tif sameId(m, pkg, name) {\n\t\t\t\treturn i, m\n\t\t\t}\n\t\t}\n\t}\n\treturn -1, nil\n}\n\nfunc sameId(obj types.Object, pkg *types.Package, name string) bool {\n\t// spec:\n\t// \"Two identifiers are different if they are spelled differently,\n\t// or if they appear in different packages and are not exported.\n\t// Otherwise, they are the same.\"\n\tif name != obj.Name() {\n\t\treturn false\n\t}\n\t// obj.Name == name\n\tif obj.Exported() {\n\t\treturn true\n\t}\n\t// not exported, so packages must be the same (pkg == nil for\n\t// fields in Universe scope; this can only happen for types\n\t// introduced via Eval)\n\tif pkg == nil || obj.Pkg() == nil {\n\t\treturn pkg == obj.Pkg()\n\t}\n\t// pkg != nil && obj.pkg != nil\n\treturn pkg.Path() == obj.Pkg().Path()\n}\n\nfunc implements(V types.Type, T *types.Interface, msV *types.MethodSet) ([]*types.Selection, bool) {\n\t// fast path for common case\n\tif T.Empty() {\n\t\treturn nil, true\n\t}\n\n\tif ityp, _ := V.Underlying().(*types.Interface); ityp != nil {\n\t\t// TODO(dh): is this code reachable?\n\t\tfor m := range T.Methods() {\n\t\t\t_, obj := lookupMethod(ityp, m.Pkg(), m.Name())\n\t\t\tswitch {\n\t\t\tcase obj == nil:\n\t\t\t\treturn nil, false\n\t\t\tcase !types.Identical(obj.Type(), m.Type()):\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t}\n\t\treturn nil, true\n\t}\n\n\t// A concrete type implements T if it implements all methods of T.\n\tvar sels []*types.Selection\n\tvar c methodsChecker\n\tfor m := range T.Methods() {\n\t\tsel := msV.Lookup(m.Pkg(), m.Name())\n\t\tif sel == nil {\n\t\t\treturn nil, false\n\t\t}\n\n\t\tf, _ := sel.Obj().(*types.Func)\n\t\tif f == nil {\n\t\t\treturn nil, false\n\t\t}\n\n\t\tif !c.methodIsCompatible(f, m) {\n\t\t\treturn nil, false\n\t\t}\n\n\t\tsels = append(sels, sel)\n\t}\n\treturn sels, true\n}\n\ntype methodsChecker struct {\n\ttypeParams map[*types.TypeParam]types.Type\n}\n\n// Currently, this doesn't support methods like `foo(x []T)`.\nfunc (c *methodsChecker) methodIsCompatible(implFunc *types.Func, interfaceFunc *types.Func) bool {\n\tif types.Identical(implFunc.Type(), interfaceFunc.Type()) {\n\t\treturn true\n\t}\n\timplSig, implOk := implFunc.Type().(*types.Signature)\n\tinterfaceSig, interfaceOk := interfaceFunc.Type().(*types.Signature)\n\tif !implOk || !interfaceOk {\n\t\t// probably not reachable. handle conservatively.\n\t\treturn false\n\t}\n\n\tif !c.typesAreCompatible(implSig.Params(), interfaceSig.Params()) {\n\t\treturn false\n\t}\n\n\tif !c.typesAreCompatible(implSig.Results(), interfaceSig.Results()) {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (c *methodsChecker) typesAreCompatible(implTypes, interfaceTypes *types.Tuple) bool {\n\tif implTypes.Len() != interfaceTypes.Len() {\n\t\treturn false\n\t}\n\tfor i := 0; i < implTypes.Len(); i++ {\n\t\tif !c.typeIsCompatible(implTypes.At(i).Type(), interfaceTypes.At(i).Type()) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (c *methodsChecker) typeIsCompatible(implType, interfaceType types.Type) bool {\n\tif types.Identical(implType, interfaceType) {\n\t\treturn true\n\t}\n\t// We only support trivial use of type parameters. This isn't fully compatible with compiler type checking yet.\n\ttp, ok := interfaceType.(*types.TypeParam)\n\tif !ok {\n\t\treturn false\n\t}\n\tif c.typeParams == nil {\n\t\tc.typeParams = make(map[*types.TypeParam]types.Type)\n\t}\n\tif c.typeParams[tp] == nil {\n\t\tif !satisfiesConstraint(implType, tp) {\n\t\t\treturn false\n\t\t}\n\t\tc.typeParams[tp] = implType\n\t\treturn true\n\t}\n\treturn types.Identical(c.typeParams[tp], implType)\n}\n\nfunc satisfiesConstraint(t types.Type, tp *types.TypeParam) bool {\n\tbound := tp.Constraint().Underlying().(*types.Interface)\n\treturn types.Satisfies(t, bound)\n}\n"
  },
  {
    "path": "unused/runtime.go",
    "content": "package unused\n\n// Functions defined in the Go runtime that may be called through\n// compiler magic or via assembly.\nvar runtimeFuncs = map[string]bool{\n\t// Copied from cmd/compile/internal/typecheck/builtin.go, var runtimeDecls\n\t\"newobject\":               true,\n\t\"panicindex\":              true,\n\t\"panicslice\":              true,\n\t\"panicdivide\":             true,\n\t\"panicmakeslicelen\":       true,\n\t\"throwinit\":               true,\n\t\"panicwrap\":               true,\n\t\"gopanic\":                 true,\n\t\"gorecover\":               true,\n\t\"goschedguarded\":          true,\n\t\"printbool\":               true,\n\t\"printfloat\":              true,\n\t\"printint\":                true,\n\t\"printhex\":                true,\n\t\"printuint\":               true,\n\t\"printcomplex\":            true,\n\t\"printstring\":             true,\n\t\"printpointer\":            true,\n\t\"printiface\":              true,\n\t\"printeface\":              true,\n\t\"printslice\":              true,\n\t\"printnl\":                 true,\n\t\"printsp\":                 true,\n\t\"printlock\":               true,\n\t\"printunlock\":             true,\n\t\"concatstring2\":           true,\n\t\"concatstring3\":           true,\n\t\"concatstring4\":           true,\n\t\"concatstring5\":           true,\n\t\"concatstrings\":           true,\n\t\"cmpstring\":               true,\n\t\"intstring\":               true,\n\t\"slicebytetostring\":       true,\n\t\"slicebytetostringtmp\":    true,\n\t\"slicerunetostring\":       true,\n\t\"stringtoslicebyte\":       true,\n\t\"stringtoslicerune\":       true,\n\t\"slicecopy\":               true,\n\t\"slicestringcopy\":         true,\n\t\"decoderune\":              true,\n\t\"countrunes\":              true,\n\t\"convI2I\":                 true,\n\t\"convT16\":                 true,\n\t\"convT32\":                 true,\n\t\"convT64\":                 true,\n\t\"convTstring\":             true,\n\t\"convTslice\":              true,\n\t\"convT2E\":                 true,\n\t\"convT2Enoptr\":            true,\n\t\"convT2I\":                 true,\n\t\"convT2Inoptr\":            true,\n\t\"assertE2I\":               true,\n\t\"assertE2I2\":              true,\n\t\"assertI2I\":               true,\n\t\"assertI2I2\":              true,\n\t\"panicdottypeE\":           true,\n\t\"panicdottypeI\":           true,\n\t\"panicnildottype\":         true,\n\t\"ifaceeq\":                 true,\n\t\"efaceeq\":                 true,\n\t\"fastrand\":                true,\n\t\"makemap64\":               true,\n\t\"makemap\":                 true,\n\t\"makemap_small\":           true,\n\t\"mapaccess1\":              true,\n\t\"mapaccess1_fast32\":       true,\n\t\"mapaccess1_fast64\":       true,\n\t\"mapaccess1_faststr\":      true,\n\t\"mapaccess1_fat\":          true,\n\t\"mapaccess2\":              true,\n\t\"mapaccess2_fast32\":       true,\n\t\"mapaccess2_fast64\":       true,\n\t\"mapaccess2_faststr\":      true,\n\t\"mapaccess2_fat\":          true,\n\t\"mapassign\":               true,\n\t\"mapassign_fast32\":        true,\n\t\"mapassign_fast32ptr\":     true,\n\t\"mapassign_fast64\":        true,\n\t\"mapassign_fast64ptr\":     true,\n\t\"mapassign_faststr\":       true,\n\t\"mapiterinit\":             true,\n\t\"mapdelete\":               true,\n\t\"mapdelete_fast32\":        true,\n\t\"mapdelete_fast64\":        true,\n\t\"mapdelete_faststr\":       true,\n\t\"mapiternext\":             true,\n\t\"mapclear\":                true,\n\t\"makechan64\":              true,\n\t\"makechan\":                true,\n\t\"chanrecv1\":               true,\n\t\"chanrecv2\":               true,\n\t\"chansend1\":               true,\n\t\"closechan\":               true,\n\t\"writeBarrier\":            true,\n\t\"typedmemmove\":            true,\n\t\"typedmemclr\":             true,\n\t\"typedslicecopy\":          true,\n\t\"selectnbsend\":            true,\n\t\"selectnbrecv\":            true,\n\t\"selectnbrecv2\":           true,\n\t\"selectsetpc\":             true,\n\t\"selectgo\":                true,\n\t\"block\":                   true,\n\t\"makeslice\":               true,\n\t\"makeslice64\":             true,\n\t\"growslice\":               true,\n\t\"memmove\":                 true,\n\t\"memclrNoHeapPointers\":    true,\n\t\"memclrHasPointers\":       true,\n\t\"memequal\":                true,\n\t\"memequal8\":               true,\n\t\"memequal16\":              true,\n\t\"memequal32\":              true,\n\t\"memequal64\":              true,\n\t\"memequal128\":             true,\n\t\"int64div\":                true,\n\t\"uint64div\":               true,\n\t\"int64mod\":                true,\n\t\"uint64mod\":               true,\n\t\"float64toint64\":          true,\n\t\"float64touint64\":         true,\n\t\"float64touint32\":         true,\n\t\"int64tofloat64\":          true,\n\t\"uint64tofloat64\":         true,\n\t\"uint32tofloat64\":         true,\n\t\"complex128div\":           true,\n\t\"racefuncenter\":           true,\n\t\"racefuncenterfp\":         true,\n\t\"racefuncexit\":            true,\n\t\"raceread\":                true,\n\t\"racewrite\":               true,\n\t\"racereadrange\":           true,\n\t\"racewriterange\":          true,\n\t\"msanread\":                true,\n\t\"msanwrite\":               true,\n\t\"x86HasPOPCNT\":            true,\n\t\"x86HasSSE41\":             true,\n\t\"arm64HasATOMICS\":         true,\n\t\"mallocgc\":                true,\n\t\"panicshift\":              true,\n\t\"panicmakeslicecap\":       true,\n\t\"goPanicIndex\":            true,\n\t\"goPanicIndexU\":           true,\n\t\"goPanicSliceAlen\":        true,\n\t\"goPanicSliceAlenU\":       true,\n\t\"goPanicSliceAcap\":        true,\n\t\"goPanicSliceAcapU\":       true,\n\t\"goPanicSliceB\":           true,\n\t\"goPanicSliceBU\":          true,\n\t\"goPanicSlice3Alen\":       true,\n\t\"goPanicSlice3AlenU\":      true,\n\t\"goPanicSlice3Acap\":       true,\n\t\"goPanicSlice3AcapU\":      true,\n\t\"goPanicSlice3B\":          true,\n\t\"goPanicSlice3BU\":         true,\n\t\"goPanicSlice3C\":          true,\n\t\"goPanicSlice3CU\":         true,\n\t\"goPanicSliceConvert\":     true,\n\t\"printuintptr\":            true,\n\t\"convT\":                   true,\n\t\"convTnoptr\":              true,\n\t\"makeslicecopy\":           true,\n\t\"unsafeslicecheckptr\":     true,\n\t\"panicunsafeslicelen\":     true,\n\t\"panicunsafeslicenilptr\":  true,\n\t\"unsafestringcheckptr\":    true,\n\t\"panicunsafestringlen\":    true,\n\t\"panicunsafestringnilptr\": true,\n\t\"mulUintptr\":              true,\n\t\"memequal0\":               true,\n\t\"f32equal\":                true,\n\t\"f64equal\":                true,\n\t\"c64equal\":                true,\n\t\"c128equal\":               true,\n\t\"strequal\":                true,\n\t\"interequal\":              true,\n\t\"nilinterequal\":           true,\n\t\"memhash\":                 true,\n\t\"memhash0\":                true,\n\t\"memhash8\":                true,\n\t\"memhash16\":               true,\n\t\"memhash32\":               true,\n\t\"memhash64\":               true,\n\t\"memhash128\":              true,\n\t\"f32hash\":                 true,\n\t\"f64hash\":                 true,\n\t\"c64hash\":                 true,\n\t\"c128hash\":                true,\n\t\"strhash\":                 true,\n\t\"interhash\":               true,\n\t\"nilinterhash\":            true,\n\t\"int64tofloat32\":          true,\n\t\"uint64tofloat32\":         true,\n\t\"getcallerpc\":             true,\n\t\"getcallersp\":             true,\n\t\"msanmove\":                true,\n\t\"asanread\":                true,\n\t\"asanwrite\":               true,\n\t\"checkptrAlignment\":       true,\n\t\"checkptrArithmetic\":      true,\n\t\"libfuzzerTraceCmp1\":      true,\n\t\"libfuzzerTraceCmp2\":      true,\n\t\"libfuzzerTraceCmp4\":      true,\n\t\"libfuzzerTraceCmp8\":      true,\n\t\"libfuzzerTraceConstCmp1\": true,\n\t\"libfuzzerTraceConstCmp2\": true,\n\t\"libfuzzerTraceConstCmp4\": true,\n\t\"libfuzzerTraceConstCmp8\": true,\n\t\"libfuzzerHookStrCmp\":     true,\n\t\"libfuzzerHookEqualFold\":  true,\n\t\"addCovMeta\":              true,\n\t\"x86HasFMA\":               true,\n\t\"armHasVFPv4\":             true,\n\n\t// Extracted from assembly code in the standard library, with the exception of the runtime package itself\n\t\"abort\":                 true,\n\t\"aeshashbody\":           true,\n\t\"args\":                  true,\n\t\"asminit\":               true,\n\t\"badctxt\":               true,\n\t\"badmcall2\":             true,\n\t\"badmcall\":              true,\n\t\"badmorestackg0\":        true,\n\t\"badmorestackgsignal\":   true,\n\t\"badsignal2\":            true,\n\t\"callbackasm1\":          true,\n\t\"callCfunction\":         true,\n\t\"cgocallback_gofunc\":    true,\n\t\"cgocallbackg\":          true,\n\t\"checkgoarm\":            true,\n\t\"check\":                 true,\n\t\"debugCallCheck\":        true,\n\t\"debugCallWrap\":         true,\n\t\"emptyfunc\":             true,\n\t\"entersyscall\":          true,\n\t\"exit\":                  true,\n\t\"exits\":                 true,\n\t\"exitsyscall\":           true,\n\t\"externalthreadhandler\": true,\n\t\"findnull\":              true,\n\t\"goexit1\":               true,\n\t\"gostring\":              true,\n\t\"i386_set_ldt\":          true,\n\t\"_initcgo\":              true,\n\t\"init_thread_tls\":       true,\n\t\"ldt0setup\":             true,\n\t\"libpreinit\":            true,\n\t\"load_g\":                true,\n\t\"morestack\":             true,\n\t\"mstart\":                true,\n\t\"nacl_sysinfo\":          true,\n\t\"nanotimeQPC\":           true,\n\t\"nanotime\":              true,\n\t\"newosproc0\":            true,\n\t\"newproc\":               true,\n\t\"newstack\":              true,\n\t\"noted\":                 true,\n\t\"nowQPC\":                true,\n\t\"osinit\":                true,\n\t\"printf\":                true,\n\t\"racecallback\":          true,\n\t\"reflectcallmove\":       true,\n\t\"reginit\":               true,\n\t\"rt0_go\":                true,\n\t\"save_g\":                true,\n\t\"schedinit\":             true,\n\t\"setldt\":                true,\n\t\"settls\":                true,\n\t\"sighandler\":            true,\n\t\"sigprofNonGo\":          true,\n\t\"sigtrampgo\":            true,\n\t\"_sigtramp\":             true,\n\t\"sigtramp\":              true,\n\t\"stackcheck\":            true,\n\t\"syscall_chdir\":         true,\n\t\"syscall_chroot\":        true,\n\t\"syscall_close\":         true,\n\t\"syscall_dup2\":          true,\n\t\"syscall_execve\":        true,\n\t\"syscall_exit\":          true,\n\t\"syscall_fcntl\":         true,\n\t\"syscall_forkx\":         true,\n\t\"syscall_gethostname\":   true,\n\t\"syscall_getpid\":        true,\n\t\"syscall_ioctl\":         true,\n\t\"syscall_pipe\":          true,\n\t\"syscall_rawsyscall6\":   true,\n\t\"syscall_rawSyscall6\":   true,\n\t\"syscall_rawsyscall\":    true,\n\t\"syscall_RawSyscall\":    true,\n\t\"syscall_rawsysvicall6\": true,\n\t\"syscall_setgid\":        true,\n\t\"syscall_setgroups\":     true,\n\t\"syscall_setpgid\":       true,\n\t\"syscall_setsid\":        true,\n\t\"syscall_setuid\":        true,\n\t\"syscall_syscall6\":      true,\n\t\"syscall_syscall\":       true,\n\t\"syscall_Syscall\":       true,\n\t\"syscall_sysvicall6\":    true,\n\t\"syscall_wait4\":         true,\n\t\"syscall_write\":         true,\n\t\"traceback\":             true,\n\t\"tstart\":                true,\n\t\"usplitR0\":              true,\n\t\"wbBufFlush\":            true,\n\t\"write\":                 true,\n\n\t// Other runtime functions that can get called in non-standard ways\n\t\"bgsweep\":             true,\n\t\"memhash_varlen\":      true,\n\t\"strhashFallback\":     true,\n\t\"asanregisterglobals\": true,\n\t\"cgoUse\":              true,\n\t\"cgoCheckPointer\":     true,\n\t\"cgoCheckResult\":      true,\n\t\"_cgo_panic_internal\": true,\n\t\"addExitHook\":         true,\n}\n\nvar runtimeCoverageFuncs = map[string]bool{\n\t\"initHook\":            true,\n\t\"markProfileEmitted\":  true,\n\t\"processCoverTestDir\": true,\n}\n"
  },
  {
    "path": "unused/serialize.go",
    "content": "package unused\n\nimport (\n\t\"fmt\"\n\t\"go/token\"\n\t\"os\"\n\n\t\"golang.org/x/tools/go/types/objectpath\"\n)\n\ntype ObjectPath struct {\n\tPkgPath string\n\tObjPath objectpath.Path\n}\n\n// XXX make sure that node 0 always exists and is always the root\n\ntype SerializedGraph struct {\n\tnodes       []Node\n\tnodesByPath map[ObjectPath]NodeID\n\t// XXX deduplicating on position is dubious for `switch x := foo.(type)`, where x will be declared many times for\n\t// the different types, but all at the same position. On the other hand, merging these nodes is probably fine.\n\tnodesByPosition map[token.Position]NodeID\n}\n\nfunc trace(f string, args ...any) {\n\tfmt.Fprintf(os.Stderr, f, args...)\n\tfmt.Fprintln(os.Stderr)\n}\n\nfunc (g *SerializedGraph) Merge(nodes []Node) {\n\tif g.nodesByPath == nil {\n\t\tg.nodesByPath = map[ObjectPath]NodeID{}\n\t}\n\tif g.nodesByPosition == nil {\n\t\tg.nodesByPosition = map[token.Position]NodeID{}\n\t}\n\tif len(g.nodes) == 0 {\n\t\t// Seed nodes with a root node\n\t\tg.nodes = append(g.nodes, Node{})\n\t}\n\t// OPT(dh): reuse storage between calls to Merge\n\tremapping := make([]NodeID, len(nodes))\n\n\t// First pass: compute remapping of IDs of to-be-merged nodes\n\tfor _, n := range nodes {\n\t\t// XXX Column is never 0. it's 1 if there is no column information in the export data. which sucks, because\n\t\t// objects can also genuinely be in column 1.\n\t\tif n.id != 0 && n.obj.Path == (ObjectPath{}) && n.obj.Position.Column == 0 {\n\t\t\t// If the object has no path, then it couldn't have come from export data, which means it needs to have full\n\t\t\t// position information including a column.\n\t\t\tpanic(fmt.Sprintf(\"object %q has no path but also no column information\", n.obj.Name))\n\t\t}\n\n\t\tif orig, ok := g.nodesByPath[n.obj.Path]; ok {\n\t\t\t// We already have a node for this object\n\t\t\ttrace(\"deduplicating %d -> %d based on path %s\", n.id, orig, n.obj.Path)\n\t\t\tremapping[n.id] = orig\n\t\t} else if orig, ok := g.nodesByPosition[n.obj.Position]; ok && n.obj.Position.Column != 0 {\n\t\t\t// We already have a node for this object\n\t\t\ttrace(\"deduplicating %d -> %d based on position %s\", n.id, orig, n.obj.Position)\n\t\t\tremapping[n.id] = orig\n\t\t} else {\n\t\t\t// This object is new to us; change ID to avoid collision\n\t\t\tnewID := NodeID(len(g.nodes))\n\t\t\ttrace(\"new node, remapping %d -> %d\", n.id, newID)\n\t\t\tremapping[n.id] = newID\n\t\t\tg.nodes = append(g.nodes, Node{\n\t\t\t\tid:   newID,\n\t\t\t\tobj:  n.obj,\n\t\t\t\tuses: make([]NodeID, 0, len(n.uses)),\n\t\t\t\towns: make([]NodeID, 0, len(n.owns)),\n\t\t\t})\n\t\t\tif n.id == 0 {\n\t\t\t\t// Our root uses all the roots of the subgraphs\n\t\t\t\tg.nodes[0].uses = append(g.nodes[0].uses, newID)\n\t\t\t}\n\t\t\tif n.obj.Path != (ObjectPath{}) {\n\t\t\t\tg.nodesByPath[n.obj.Path] = newID\n\t\t\t}\n\t\t\tif n.obj.Position.Column != 0 {\n\t\t\t\tg.nodesByPosition[n.obj.Position] = newID\n\t\t\t}\n\t\t}\n\t}\n\n\t// Second step: apply remapping\n\tfor _, n := range nodes {\n\t\tn.id = remapping[n.id]\n\t\tfor i := range n.uses {\n\t\t\tn.uses[i] = remapping[n.uses[i]]\n\t\t}\n\t\tfor i := range n.owns {\n\t\t\tn.owns[i] = remapping[n.owns[i]]\n\t\t}\n\t\tg.nodes[n.id].uses = append(g.nodes[n.id].uses, n.uses...)\n\t\tg.nodes[n.id].owns = append(g.nodes[n.id].owns, n.owns...)\n\t}\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/alias/alias.go",
    "content": "package main\n\nimport \"net/http\"\n\ntype t1 struct{} //@ used(\"t1\", true)\ntype t2 struct{} //@ used(\"t2\", false)\ntype t3 struct{} //@ used(\"t3\", true)\n\ntype alias1 = t1  //@ used(\"alias1\", true)\ntype alias2 = t2  //@ used(\"alias2\", false)\ntype alias3 = t3  //@ used(\"alias3\", false)\ntype alias4 = int //@ used(\"alias4\", false)\n\nfunc main() { //@ used(\"main\", true)\n\tvar _ alias1 //@ used(\"_\", true)\n\tvar _ t3     //@ used(\"_\", true)\n}\n\ntype t4 struct { //@ used(\"t4\", true)\n\tx int //@ used(\"x\", true)\n}\n\nfunc (t4) foo() {} //@ used(\"foo\", true)\n\n//lint:ignore U1000 alias5 is ignored, which also ignores t4\ntype alias5 = t4 //@ used(\"alias5\", true)\n\n//lint:ignore U1000 alias6 is ignored, and we don't incorrectly try to include http.Server's fields and methods in the graph\ntype alias6 = http.Server //@ used(\"alias6\", true)\n\n//lint:ignore U1000 aliases don't have to be to named types\ntype alias7 = struct { //@ used(\"alias7\", true)\n\tx int //@ used(\"x\", true)\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/anonymous/anonymous.go",
    "content": "package pkg\n\nimport \"fmt\"\n\ntype Node interface { //@ used(\"Node\", true)\n\tposition() int //@ used(\"position\", true)\n}\n\ntype noder struct{} //@ used(\"noder\", true)\n\nfunc (noder) position() int { panic(\"unreachable\") } //@ used(\"position\", true)\n\nfunc Fn() { //@ used(\"Fn\", true)\n\tnodes := []Node{struct { //@ used(\"nodes\", true)\n\t\tnoder //@ used(\"noder\", true)\n\t}{}}\n\tfmt.Println(nodes)\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/blank/blank.go",
    "content": "package pkg\n\nimport _ \"fmt\"\n\ntype t1 struct{} //@ used(\"t1\", false)\ntype t2 struct { //@ used(\"t2\", true)\n\t_ int //@ used(\"_\", true)\n}\ntype t3 struct{} //@ used(\"t3\", true)\ntype t4 struct{} //@ used(\"t4\", true)\ntype t5 struct{} //@ used(\"t5\", true)\n\nvar _ = t2{} //@ used(\"_\", true)\n\nfunc fn1() { //@ used(\"fn1\", false)\n\t_ = t1{}\n\tvar _ = t1{} //@ quiet(\"_\")\n}\n\nfunc fn2() { //@ used(\"fn2\", true)\n\t_ = t3{}\n\tvar _ t4        //@ used(\"_\", true)\n\tvar _ *t5 = nil //@ used(\"_\", true)\n}\n\nfunc init() { //@ used(\"init\", true)\n\tfn2()\n}\n\nfunc _() {} //@ used(\"_\", true)\n\ntype _ struct{} //@ used(\"_\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/blank-function-parameter/blank.go",
    "content": "package pkg\n\ntype customType int //@ used(\"customType\", true)\n\nfunc Foo(customType) {} //@ used(\"Foo\", true), used(\"\", true)\nfunc bar(customType) {} //@ used(\"bar\", false), quiet(\"\")\n"
  },
  {
    "path": "unused/testdata/src/example.com/cgo/cgo.go",
    "content": "package pkg\n\n//go:cgo_export_dynamic\nfunc foo() {} //@ used(\"foo\", true)\n\nfunc bar() {} //@ used(\"bar\", false)\n"
  },
  {
    "path": "unused/testdata/src/example.com/constexpr/constexpr.go",
    "content": "package pkg\n\nimport (\n\t\"io\"\n\t\"unsafe\"\n)\n\n// https://staticcheck.dev/issues/812\n\nvar (\n\tw  io.Writer          //@ used(\"w\", true)\n\tsz = unsafe.Sizeof(w) //@ used(\"sz\", true)\n)\n\nvar _ = sz //@ used(\"_\", true)\n\ntype t struct { //@ used(\"t\", true)\n\tF int //@ used(\"F\", true)\n}\n\nconst S = unsafe.Sizeof(t{}) //@ used(\"S\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/consts/consts.go",
    "content": "package pkg\n\nconst c1 = 1 //@ used(\"c1\", true)\n\nconst c2 = 1 //@ used(\"c2\", true)\nconst c3 = 1 //@ used(\"c3\", true)\nconst c4 = 1 //@ used(\"c4\", true)\nconst C5 = 1 //@ used(\"C5\", true)\n\nconst (\n\tc6 = 0 //@ used(\"c6\", true)\n\tc7     //@ used(\"c7\", true)\n\tc8     //@ used(\"c8\", true)\n\n\tc9  //@ used(\"c9\", false)\n\tc10 //@ used(\"c10\", false)\n\tc11 //@ used(\"c11\", false)\n)\n\n// constants named _ are used, but are not part of constant groups\nconst (\n\tc12 = 0 //@ used(\"c12\", false)\n\t_       //@ used(\"_\", true)\n\tc13     //@ used(\"c13\", false)\n)\n\nvar _ = []int{c3: 1} //@ used(\"_\", true)\n\ntype T1 struct { //@ used(\"T1\", true)\n\tF1 [c1]int //@ used(\"F1\", true)\n}\n\nfunc init() { //@ used(\"init\", true)\n\t_ = []int{c2: 1}\n\tvar _ [c4]int //@ used(\"_\", true)\n\n\t_ = c7\n}\n\nfunc Fn() { //@ used(\"Fn\", true)\n\tconst X = 1 //@ used(\"X\", false)\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/conversion/conversion.go",
    "content": "package pkg\n\nimport (\n\t\"compress/flate\"\n\t\"unsafe\"\n)\n\ntype t1 struct { //@ used(\"t1\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n}\n\ntype t2 struct { //@ used(\"t2\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n}\n\ntype t3 struct { //@ used(\"t3\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", false)\n}\n\ntype t4 struct { //@ used(\"t4\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", false)\n}\n\ntype t5 struct { //@ used(\"t5\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n}\n\ntype t6 struct { //@ used(\"t6\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n}\n\ntype t7 struct { //@ used(\"t7\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n}\n\ntype t8 struct { //@ used(\"t8\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n}\n\ntype t9 struct { //@ used(\"t9\", true)\n\tOffset int64 //@ used(\"Offset\", true)\n\tErr    error //@ used(\"Err\", true)\n}\n\ntype t10 struct { //@ used(\"t10\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n}\n\nfunc fn() { //@ used(\"fn\", true)\n\t// All fields in t2 used because they're initialised in t1\n\tv1 := t1{0, 1} //@ used(\"v1\", true)\n\tv2 := t2(v1)   //@ used(\"v2\", true)\n\t_ = v2\n\n\t// Field b isn't used by anyone\n\tv3 := t3{}   //@ used(\"v3\", true)\n\tv4 := t4(v3) //@ used(\"v4\", true)\n\tprintln(v3.a)\n\t_ = v4\n\n\t// Both fields are used\n\tv5 := t5{}   //@ used(\"v5\", true)\n\tv6 := t6(v5) //@ used(\"v6\", true)\n\tprintln(v5.a)\n\tprintln(v6.b)\n\n\tv7 := &t7{} //@ used(\"v7\", true)\n\tprintln(v7.a)\n\tprintln(v7.b)\n\tv8 := (*t8)(v7) //@ used(\"v8\", true)\n\t_ = v8\n\n\tvb := flate.ReadError{} //@ used(\"vb\", true)\n\tv9 := t9(vb)            //@ used(\"v9\", true)\n\t_ = v9\n\n\t// All fields are used because this is an unsafe conversion\n\tvar b []byte                         //@ used(\"b\", true)\n\tv10 := (*t10)(unsafe.Pointer(&b[0])) //@ used(\"v10\", true)\n\t_ = v10\n}\n\nfunc init() { fn() } //@ used(\"init\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/cyclic/cyclic.go",
    "content": "package pkg\n\nfunc a() { //@ used(\"a\", false)\n\tb()\n}\n\nfunc b() { //@ used(\"b\", false)\n\ta()\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/defer/defer.go",
    "content": "package pkg\n\ntype t struct{} //@ used(\"t\", true)\n\nfunc (t) fn1() {} //@ used(\"fn1\", true)\nfunc (t) fn2() {} //@ used(\"fn2\", true)\nfunc fn1()     {} //@ used(\"fn1\", true)\nfunc fn2()     {} //@ used(\"fn2\", true)\n\nfunc Fn() { //@ used(\"Fn\", true)\n\tvar v t //@ used(\"v\", true)\n\tdefer fn1()\n\tdefer v.fn1()\n\tgo fn2()\n\tgo v.fn2()\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/elem/elem.go",
    "content": "// Test of field usage detection\n\npackage pkg\n\ntype t15 struct { //@ used(\"t15\", true)\n\tf151 int //@ used(\"f151\", true)\n}\ntype a2 [1]t15 //@ used(\"a2\", true)\n\ntype t16 struct{} //@ used(\"t16\", true)\ntype a3 [1][1]t16 //@ used(\"a3\", true)\n\nfunc foo() { //@ used(\"foo\", true)\n\t_ = a2{0: {1}}\n\t_ = a3{{{}}}\n}\n\nfunc init() { foo() } //@ used(\"init\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/embedded_call/embedded_call.go",
    "content": "package pkg\n\nvar t1 struct { //@ used(\"t1\", true)\n\tt2 //@ used(\"t2\", true)\n\tt3 //@ used(\"t3\", true)\n\tt4 //@ used(\"t4\", true)\n}\n\ntype t2 struct{} //@ used(\"t2\", true)\ntype t3 struct{} //@ used(\"t3\", true)\ntype t4 struct { //@ used(\"t4\", true)\n\tt5 //@ used(\"t5\", true)\n}\ntype t5 struct{} //@ used(\"t5\", true)\n\nfunc (t2) foo() {} //@ used(\"foo\", true)\nfunc (t3) bar() {} //@ used(\"bar\", true)\nfunc (t5) baz() {} //@ used(\"baz\", true)\nfunc init() { //@ used(\"init\", true)\n\tt1.foo()\n\t_ = t1.bar\n\tt1.baz()\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/embedding/embedding.go",
    "content": "package pkg\n\ntype I interface { //@ used(\"I\", true)\n\tf1() //@ used(\"f1\", true)\n\tf2() //@ used(\"f2\", true)\n}\n\nfunc init() { //@ used(\"init\", true)\n\tvar _ I //@ used(\"_\", true)\n}\n\ntype t1 struct{} //@ used(\"t1\", true)\ntype T2 struct { //@ used(\"T2\", true)\n\tt1 //@ used(\"t1\", true)\n}\n\nfunc (t1) f1() {} //@ used(\"f1\", true)\nfunc (T2) f2() {} //@ used(\"f2\", true)\n\nfunc Fn() { //@ used(\"Fn\", true)\n\tvar v T2 //@ used(\"v\", true)\n\t_ = v.t1\n}\n\ntype I2 interface { //@ used(\"I2\", true)\n\tf3() //@ used(\"f3\", true)\n\tf4() //@ used(\"f4\", true)\n}\n\ntype t3 struct{} //@ used(\"t3\", true)\ntype t4 struct { //@ used(\"t4\", true)\n\tx  int //@ used(\"x\", false)\n\ty  int //@ used(\"y\", false)\n\tt3     //@ used(\"t3\", true)\n}\n\nfunc (*t3) f3() {} //@ used(\"f3\", true)\nfunc (*t4) f4() {} //@ used(\"f4\", true)\n\nfunc init() { //@ used(\"init\", true)\n\tvar i I2 = &t4{} //@ used(\"i\", true)\n\ti.f3()\n\ti.f4()\n}\n\ntype i3 interface { //@ used(\"i3\", true)\n\tF() //@ used(\"F\", true)\n}\n\ntype I4 interface { //@ used(\"I4\", true)\n\ti3\n}\n\ntype T5 struct { //@ used(\"T5\", true)\n\tt6 //@ used(\"t6\", true)\n}\n\ntype t6 struct { //@ used(\"t6\", true)\n\tF int //@ used(\"F\", true)\n}\n\ntype t7 struct { //@ used(\"t7\", true)\n\tX int //@ used(\"X\", true)\n}\ntype t8 struct { //@ used(\"t8\", true)\n\tt7 //@ used(\"t7\", true)\n}\ntype t9 struct { //@ used(\"t9\", true)\n\tt8 //@ used(\"t8\", true)\n}\n\nvar _ = t9{} //@ used(\"_\", true)\n\ntype t10 struct{} //@ used(\"t10\", true)\n\nfunc (*t10) Foo() {} //@ used(\"Foo\", true)\n\ntype t11 struct { //@ used(\"t11\", true)\n\tt10 //@ used(\"t10\", true)\n}\n\nvar _ = t11{} //@ used(\"_\", true)\n\ntype i5 interface{} //@ used(\"i5\", true)\ntype I6 interface { //@ used(\"I6\", true)\n\ti5\n}\n\n// When recursively looking for embedded exported fields, don't visit top-level type again\ntype t12 struct { //@ used(\"t12\", true)\n\t*t12     //@ used(\"t12\", false)\n\tF    int //@ used(\"F\", true)\n}\n\nvar _ = t12{} //@ used(\"_\", true)\n\n// embedded fields whose names are exported are used, same as normal exported fields.\ntype T13 struct { //@ used(\"T13\", true)\n\tT14  //@ used(\"T14\", true)\n\t*T15 //@ used(\"T15\", true)\n}\n\ntype T14 struct{} //@ used(\"T14\", true)\ntype T15 struct{} //@ used(\"T15\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/embedding-alias/embedding-alias.go",
    "content": "package pkg\n\ntype s1 struct{} //@ used(\"s1\", true)\n\n// Make sure the alias is used, and not just the type it points to.\ntype a1 = s1 //@ used(\"a1\", true)\n\ntype E1 struct { //@ used(\"E1\", true)\n\ta1 //@ used(\"a1\", true)\n}\n\nfunc F1(e E1) { //@ used(\"F1\", true), used(\"e\", true)\n\t_ = e.a1\n}\n\n// Make sure fields get used correctly when embedded multiple times\ntype s2 struct { //@ used(\"s2\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n\tc int //@ used(\"c\", false)\n}\n\ntype a2 = s2 //@ used(\"a2\", true)\n\ntype E2 struct { //@ used(\"E2\", true)\n\ta2 //@ used(\"a2\", true)\n}\n\ntype E3 struct { //@ used(\"E3\", true)\n\ta2 //@ used(\"a2\", true)\n}\n\nfunc F2(e E2) { //@ used(\"F2\", true), used(\"e\", true)\n\t_ = e.a\n}\n\nfunc F3(e E3) { //@ used(\"F3\", true), used(\"e\", true)\n\t_ = e.b\n}\n\n// Make sure embedding aliases to unnamed types works\ntype a4 = struct { //@ used(\"a4\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n\tc int //@ used(\"c\", false)\n}\n\ntype E4 struct { //@ used(\"E4\", true)\n\ta4 //@ used(\"a4\", true)\n}\n\ntype E5 struct { //@ used(\"E5\", true)\n\ta4 //@ used(\"a4\", true)\n}\n\nfunc F4(e E4) { //@ used(\"F4\", true), used(\"e\", true)\n\t_ = e.a\n}\n\nfunc F5(e E5) { //@ used(\"F5\", true), used(\"e\", true)\n\t_ = e.b\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/embedding2/embedding2.go",
    "content": "package main\n\ntype AA interface { //@ used(\"AA\", true)\n\tA() //@ used(\"A\", true)\n}\n\ntype BB interface { //@ used(\"BB\", true)\n\tAA\n}\n\ntype CC interface { //@ used(\"CC\", true)\n\tBB\n\tC() //@ used(\"C\", true)\n}\n\nfunc c(cc CC) { //@ used(\"c\", true), used(\"cc\", true)\n\tcc.A()\n}\n\ntype z struct{} //@ used(\"z\", true)\n\nfunc (z) A() {} //@ used(\"A\", true)\nfunc (z) B() {} //@ used(\"B\", true)\nfunc (z) C() {} //@ used(\"C\", true)\n\nfunc main() { //@ used(\"main\", true)\n\tc(z{})\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/exported_fields/exported_fields.go",
    "content": "package pkg\n\ntype t1 struct { //@ used(\"t1\", true)\n\tF1 int //@ used(\"F1\", true)\n}\n\ntype T2 struct { //@ used(\"T2\", true)\n\tF2 int //@ used(\"F2\", true)\n}\n\nvar v struct { //@ used(\"v\", true)\n\tT3 //@ used(\"T3\", true)\n}\n\ntype T3 struct{} //@ used(\"T3\", true)\n\nfunc (T3) Foo() {} //@ used(\"Foo\", true)\n\nfunc init() { //@ used(\"init\", true)\n\tv.Foo()\n}\n\nfunc init() { //@ used(\"init\", true)\n\t_ = t1{}\n}\n\ntype codeResponse struct { //@ used(\"codeResponse\", true)\n\tTree *codeNode `json:\"tree\"` //@ used(\"Tree\", true)\n}\n\ntype codeNode struct { //@ used(\"codeNode\", true)\n}\n\nfunc init() { //@ used(\"init\", true)\n\t_ = codeResponse{}\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/exported_fields_main/exported_fields_main.go",
    "content": "package main\n\ntype t1 struct { //@ used(\"t1\", true)\n\tF1 int //@ used(\"F1\", true)\n}\n\ntype T2 struct { //@ used(\"T2\", true)\n\tF2 int //@ used(\"F2\", true)\n}\n\nfunc init() { //@ used(\"init\", true)\n\t_ = t1{}\n\t_ = T2{}\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/exported_method_test/exported_method.go",
    "content": "package pkg\n"
  },
  {
    "path": "unused/testdata/src/example.com/exported_method_test/exported_method_test.go",
    "content": "package pkg\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"testing\"\n)\n\ntype countReadSeeker struct { //@ used_test(\"countReadSeeker\", true)\n\tio.ReadSeeker       //@ used_test(\"ReadSeeker\", true)\n\tN             int64 //@ used_test(\"N\", true)\n}\n\nfunc (rs *countReadSeeker) Read(buf []byte) (int, error) { //@ used_test(\"Read\", true), used_test(\"rs\", true), used_test(\"buf\", true)\n\tn, err := rs.ReadSeeker.Read(buf) //@ used_test(\"n\", true), used_test(\"err\", true)\n\trs.N += int64(n)\n\treturn n, err\n}\n\nfunc TestFoo(t *testing.T) { //@ used_test(\"TestFoo\", true), used_test(\"t\", true)\n\tr := bytes.NewReader([]byte(\"Hello, world!\")) //@ used_test(\"r\", true)\n\tcr := &countReadSeeker{ReadSeeker: r}         //@ used_test(\"cr\", true)\n\tioutil.ReadAll(cr)\n\tif cr.N != 13 {\n\t\tt.Errorf(\"got %d, want 13\", cr.N)\n\t}\n}\n\nvar sink int //@ used_test(\"sink\", true)\n\nfunc BenchmarkFoo(b *testing.B) { //@ used_test(\"BenchmarkFoo\", true), used_test(\"b\", true)\n\tfor i := 0; i < b.N; i++ { //@ used_test(\"i\", true)\n\t\tsink = fn()\n\t}\n}\n\nfunc fn() int { return 0 } //@ used_test(\"fn\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/fields/fields.go",
    "content": "// Test of field usage detection\n\npackage pkg\n\ntype t1 struct { //@ used(\"t1\", true)\n\tf11 int //@ used(\"f11\", true)\n\tf12 int //@ used(\"f12\", true)\n}\ntype t2 struct { //@ used(\"t2\", true)\n\tf21 int //@ used(\"f21\", true)\n\tf22 int //@ used(\"f22\", true)\n}\ntype t3 struct { //@ used(\"t3\", true)\n\tf31 t4 //@ used(\"f31\", true)\n}\ntype t4 struct { //@ used(\"t4\", true)\n\tf41 int //@ used(\"f41\", true)\n}\ntype t5 struct { //@ used(\"t5\", true)\n\tf51 int //@ used(\"f51\", true)\n}\ntype t6 struct { //@ used(\"t6\", true)\n\tf61 int //@ used(\"f61\", true)\n}\ntype t7 struct { //@ used(\"t7\", true)\n\tf71 int //@ used(\"f71\", true)\n}\ntype m1 map[string]t7 //@ used(\"m1\", true)\ntype t8 struct {      //@ used(\"t8\", true)\n\tf81 int //@ used(\"f81\", true)\n}\ntype t9 struct { //@ used(\"t9\", true)\n\tf91 int //@ used(\"f91\", true)\n}\ntype t10 struct { //@ used(\"t10\", true)\n\tf101 int //@ used(\"f101\", true)\n}\ntype t11 struct { //@ used(\"t11\", true)\n\tf111 int //@ used(\"f111\", true)\n}\ntype s1 []t11     //@ used(\"s1\", true)\ntype t12 struct { //@ used(\"t12\", true)\n\tf121 int //@ used(\"f121\", true)\n}\ntype s2 []t12     //@ used(\"s2\", true)\ntype t13 struct { //@ used(\"t13\", true)\n\tf131 int //@ used(\"f131\", true)\n}\ntype t14 struct { //@ used(\"t14\", true)\n\tf141 int //@ used(\"f141\", true)\n}\ntype a1 [1]t14    //@ used(\"a1\", true)\ntype t15 struct { //@ used(\"t15\", true)\n\tf151 int //@ used(\"f151\", true)\n}\ntype a2 [1]t15    //@ used(\"a2\", true)\ntype t16 struct { //@ used(\"t16\", true)\n\tf161 int //@ used(\"f161\", true)\n}\ntype t17 struct { //@ used(\"t17\", false)\n\tf171 int //@ quiet(\"f171\")\n\tf172 int //@ quiet(\"f172\")\n}\ntype t18 struct { //@ used(\"t18\", true)\n\tf181 int //@ used(\"f181\", true)\n\tf182 int //@ used(\"f182\", false)\n\tf183 int //@ used(\"f183\", false)\n}\n\ntype t19 struct { //@ used(\"t19\", true)\n\tf191 int //@ used(\"f191\", true)\n}\ntype m2 map[string]t19 //@ used(\"m2\", true)\n\ntype t20 struct { //@ used(\"t20\", true)\n\tf201 int //@ used(\"f201\", true)\n}\ntype m3 map[string]t20 //@ used(\"m3\", true)\n\ntype t21 struct { //@ used(\"t21\", true)\n\tf211 int //@ used(\"f211\", false)\n\tf212 int //@ used(\"f212\", true)\n}\ntype t22 struct { //@ used(\"t22\", false)\n\tf221 int //@ quiet(\"f221\")\n\tf222 int //@ quiet(\"f222\")\n}\n\nfunc foo() { //@ used(\"foo\", true)\n\t_ = t10{1}\n\t_ = t21{f212: 1}\n\t_ = []t1{{1, 2}}\n\t_ = t2{1, 2}\n\t_ = []struct {\n\t\ta int //@ used(\"a\", true)\n\t}{{1}}\n\n\t// XXX\n\t// _ = []struct{ foo struct{ bar int } }{{struct{ bar int }{1}}}\n\n\t_ = []t1{t1{1, 2}}\n\t_ = []t3{{t4{1}}}\n\t_ = map[string]t5{\"a\": {1}}\n\t_ = map[t6]string{{1}: \"a\"}\n\t_ = m1{\"a\": {1}}\n\t_ = map[t8]t8{{}: {1}}\n\t_ = map[t9]t9{{1}: {}}\n\t_ = s1{{1}}\n\t_ = s2{2: {1}}\n\t_ = [...]t13{{1}}\n\t_ = a1{{1}}\n\t_ = a2{0: {1}}\n\t_ = map[[1]t16]int{{{1}}: 1}\n\ty := struct { //@ used(\"y\", true)\n\t\tx int //@ used(\"x\", true)\n\t}{}\n\t_ = y\n\t_ = t18{f181: 1}\n\t_ = []m2{{\"a\": {1}}}\n\t_ = [][]m3{{{\"a\": {1}}}}\n}\n\nfunc init() { foo() } //@ used(\"init\", true)\n\nfunc superUnused() { //@ used(\"superUnused\", false)\n\tvar _ struct { //@ quiet(\"_\")\n\t\tx int //@ quiet(\"x\")\n\t}\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/fields/fields_go123.go",
    "content": "//go:build go1.23\n\npackage pkg\n\nimport \"structs\"\n\n// hostLayout isn't used because the fields of hlt5 aren't marked used because\n// the hostLayout named type doesn't trigger the structs.HostLayout logic.\ntype hostLayout structs.HostLayout        //@ used(\"hostLayout\", false)\ntype hostLayoutAlias = structs.HostLayout //@ used(\"hostLayoutAlias\", true)\n\ntype hlt1 struct { //@ used(\"hlt1\", true)\n\t_     structs.HostLayout //@ used(\"_\", true)\n\thlf11 int                //@ used(\"hlf11\", true)\n}\n\ntype hlt2 struct { //@ used(\"hlt2\", true)\n\tstructs.HostLayout     //@ used(\"HostLayout\", true)\n\thlf21              int //@ used(\"hlf21\", true)\n}\n\ntype hlt3 struct { //@ used(\"hlt3\", true)\n\t// Aliases of structs.HostLayout do mark fields used.\n\thostLayoutAlias     //@ used(\"hostLayoutAlias\", true)\n\thlf31           int //@ used(\"hlf31\", true)\n}\n\ntype hlt4 struct { //@ used(\"hlt4\", true)\n\t// Embedding a struct that itself has a structs.HostLayout field doesn't\n\t// mark this struct's fields used.\n\thlt3      //@ used(\"hlt3\", false)\n\thlf41 int //@ used(\"hlf41\", false)\n}\n\ntype hlt5 struct { //@ used(\"hlt5\", true)\n\t// Named types with underlying type structs.HostLayout don't mark fields\n\t// used.\n\thostLayout     //@ used(\"hostLayout\", false)\n\thlf51      int //@ used(\"hlf51\", false)\n}\n\ntype hlt6 struct { //@ used(\"hlt6\", false)\n\t// The fields may be used, but the overall struct isn't.\n\t_     structs.HostLayout //@ quiet(\"_\")\n\thlf61 int                //@ quiet(\"hlf61\")\n}\n\ntype hlt7 struct { //@ used(\"hlt7\", true)\n\t// Fields are used recursively, as they affect the layout\n\t_     structs.HostLayout //@ used(\"_\", true)\n\thlf71 [2]hlt7sub         //@ used(\"hlf71\", true)\n}\ntype hlt7sub struct { //@ used(\"hlt7sub\", true)\n\thlt7sub1 int //@ used(\"hlt7sub1\", true)\n}\n\nvar _ hlt1 //@ used(\"_\", true)\nvar _ hlt2 //@ used(\"_\", true)\nvar _ hlt3 //@ used(\"_\", true)\nvar _ hlt4 //@ used(\"_\", true)\nvar _ hlt5 //@ used(\"_\", true)\nvar _ hlt7 //@ used(\"_\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/functions/functions.go",
    "content": "package main\n\ntype state func() state //@ used(\"state\", true)\n\nfunc a() state { //@ used(\"a\", true)\n\treturn a\n}\n\nfunc main() { //@ used(\"main\", true)\n\tst := a //@ used(\"st\", true)\n\t_ = st()\n}\n\ntype t1 struct{} //@ used(\"t1\", false)\ntype t2 struct{} //@ used(\"t2\", true)\ntype t3 struct{} //@ used(\"t3\", true)\n\nfunc fn1() t1     { return t1{} } //@ used(\"fn1\", false)\nfunc fn2() (x t2) { return }      //@ used(\"fn2\", true), used(\"x\", true)\nfunc fn3() *t3    { return nil }  //@ used(\"fn3\", true)\n\nfunc fn4() { //@ used(\"fn4\", true)\n\tconst x = 1  //@ used(\"x\", true)\n\tconst y = 2  //@ used(\"y\", false)\n\ttype foo int //@ used(\"foo\", false)\n\ttype bar int //@ used(\"bar\", true)\n\n\t_ = x\n\t_ = bar(0)\n}\n\nfunc init() { //@ used(\"init\", true)\n\tfn2()\n\tfn3()\n\tfn4()\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/generated/generated.go",
    "content": "// Code generated by a monkey. DO NOT EDIT.\n\n// https://staticcheck.dev/issues/1333\n\npackage pkg\n\nfunc generated1() {}            //@ used(\"generated1\", true)\nfunc generated2() { normal2() } //@ used(\"generated2\", true)\n\nvar x = normal4() //@ used(\"x\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/generated/normal.go",
    "content": "package pkg\n\n// https://staticcheck.dev/issues/1333\n\nfunc normal1()     {}           //@ used(\"normal1\", false)\nfunc normal2()     {}           //@ used(\"normal2\", true)\nfunc normal3() int { return 0 } //@ used(\"normal3\", false)\nfunc normal4() int { return 0 } //@ used(\"normal4\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/generic-interfaces/generic-interfaces.go",
    "content": "package pkg\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\ntype I1[T any] interface { //@ used(\"I1\", true), used(\"T\", true)\n\tm1() T //@ used(\"m1\", true)\n}\n\ntype S1 struct{} //@ used(\"S1\", true)\nfunc (s *S1) m1() string { //@ used(\"s\", true), used(\"m1\", true)\n\treturn \"\"\n}\n\ntype I2[T any] interface { //@ used(\"I2\", true), used(\"T\", true)\n\tm2(T) //@ used(\"m2\", true)\n}\n\ntype S2 struct{} //@ used(\"S2\", true)\nfunc (s *S2) m2(p string) { //@ used(\"s\", true), used(\"p\", true), used(\"m2\", true)\n\treturn\n}\n\ntype I3[T any] interface { //@ used(\"I3\", true), used(\"T\", true)\n\tm3(T) T //@ used(\"m3\", true)\n}\n\ntype S3_1 struct{} //@ used(\"S3_1\", true)\nfunc (s *S3_1) m3(p string) string { //@ used(\"s\", true), used(\"p\", true), used(\"m3\", true)\n\treturn \"\"\n}\n\ntype S3_2 struct{} //@ used(\"S3_2\", true)\nfunc (s *S3_2) m3(p int) string { //@ quiet(\"s\"), quiet(\"p\"), used(\"m3\", false)\n\treturn \"\"\n}\n\ntype I4[T, U any] interface { //@ used(\"I4\", true), used(\"T\", true), used(\"U\", true)\n\tm4_1(T) U //@ used(\"m4_1\", true)\n\tm4_2() T  //@ used(\"m4_2\", true)\n\tm4_3() U  //@ used(\"m4_3\", true)\n}\n\ntype S4_1 struct{} //@ used(\"S4_1\", true)\nfunc (s *S4_1) m4_1(p string) int { //@ used(\"s\", true), used(\"p\", true), used(\"m4_1\", true)\n\treturn 42\n}\nfunc (s *S4_1) m4_2() string { //@ used(\"s\", true), used(\"m4_2\", true)\n\treturn \"\"\n}\nfunc (s *S4_1) m4_3() int { //@ used(\"s\", true), used(\"m4_3\", true)\n\treturn 0\n}\n\ntype S4_2 struct{} //@ used(\"S4_2\", true)\nfunc (s *S4_2) m4_1(p bool) int { //@ quiet(\"s\"), quiet(\"p\"), used(\"m4_1\", false)\n\treturn 0\n}\nfunc (s *S4_2) m4_2() string { //@ quiet(\"s\"), used(\"m4_2\", false)\n\treturn \"\"\n}\nfunc (s *S4_2) m4_3() int { //@ quiet(\"s\"), used(\"m4_3\", false)\n\treturn 0\n}\n\ntype S4_3 struct{} //@ used(\"S4_3\", true)\nfunc (s *S4_3) m4_1(p string) int { //@ quiet(\"s\"), quiet(\"p\"), used(\"m4_1\", false)\n\treturn 42\n}\nfunc (s *S4_3) m4_2() int { //@ quiet(\"s\"), used(\"m4_2\", false)\n\treturn 0\n}\n\ntype I5[T comparable, U comparable] interface { //@ used(\"I5\", true), used(\"T\", true), used(\"U\", true)\n\tm5(T) U //@ used(\"m5\", true)\n}\n\ntype S5_1 struct{} //@ used(\"S5_1\", true)\nfunc (s *S5_1) m5(p string) int { //@ used(\"s\", true), used(\"p\", true), used(\"m5\", true)\n\treturn 0\n}\n\ntype S5_2 struct{} //@ used(\"S5_2\", true)\nfunc (s *S5_2) m5(p any) int { //@ used(\"s\", true), used(\"p\", true), used(\"m5\", true)\n\treturn 0\n}\n\ntype S5_3 struct{} //@ used(\"S5_3\", true)\nfunc (s *S5_3) m5(p string) any { //@ used(\"s\", true), used(\"p\", true), used(\"m5\", true)\n\treturn 0\n}\n\ntype S5_4 struct{} //@ used(\"S5_4\", true)\nfunc (s *S5_4) m5(p string) io.Reader { //@ used(\"s\", true), used(\"p\", true), used(\"m5\", true)\n\treturn nil\n}\n\ntype I6[R io.Reader, W io.Writer] interface { //@ used(\"I6\", true), used(\"R\", true), used(\"W\", true)\n\tm6_1(R) R //@ used(\"m6_1\", true)\n\tm6_2(W) W //@ used(\"m6_2\", true)\n}\n\ntype S6_1 struct{} //@ used(\"S6_1\", true)\nfunc (s *S6_1) m6_1(p io.Reader) io.Reader { //@ used(\"s\", true), used(\"p\", true), used(\"m6_1\", true)\n\treturn p\n}\nfunc (s *S6_1) m6_2(p io.Writer) io.Writer { //@ used(\"s\", true), used(\"p\", true), used(\"m6_2\", true)\n\treturn p\n}\n\ntype S6_2 struct{} //@ used(\"S6_2\", true)\nfunc (s *S6_2) m6_1(p io.ReadCloser) io.ReadCloser { //@ used(\"s\", true), used(\"p\", true), used(\"m6_1\", true)\n\treturn p\n}\nfunc (s *S6_2) m6_2(p io.WriteCloser) io.WriteCloser { //@ used(\"s\", true), used(\"p\", true), used(\"m6_2\", true)\n\treturn p\n}\n\ntype S6_3 struct{} //@ used(\"S6_3\", true)\nfunc (s *S6_3) m6_1(p int) int { //@ quiet(\"s\"), quiet(\"p\"), used(\"m6_1\", false)\n\treturn p\n}\nfunc (s *S6_3) m6_2(p io.Writer) io.Writer { //@ quiet(\"s\"), quiet(\"p\"), used(\"m6_2\", false)\n\treturn p\n}\n\ntype S6_4 struct{} //@ used(\"S6_4\", true)\nfunc (s *S6_4) m6_1(p *os.File) *os.File { //@ used(\"s\", true), used(\"p\", true), used(\"m6_1\", true)\n\treturn p\n}\nfunc (s *S6_4) m6_2(p *os.File) *os.File { //@ used(\"s\", true), used(\"p\", true), used(\"m6_2\", true)\n\treturn p\n}\n\ntype S6_5 struct{} //@ used(\"S6_5\", true)\nfunc (s *S6_5) m6_1(p os.File) os.File { //@ quiet(\"s\"), quiet(\"p\"), used(\"m6_1\", false)\n\treturn p\n}\nfunc (s *S6_5) m6_2(p os.File) os.File { //@ quiet(\"s\"), quiet(\"p\"), used(\"m6_2\", false)\n\treturn p\n}\n\ntype I7[T ~int | ~string] interface { //@ used(\"I7\", true), used(\"T\", true)\n\tm7() T //@ used(\"m7\", true)\n}\n\ntype S7_1 struct{} //@ used(\"S7_1\", true)\nfunc (s *S7_1) m7() int { //@ used(\"s\", true), used(\"m7\", true)\n\treturn 0\n}\n\ntype S7_2 struct{} //@ used(\"S7_2\", true)\nfunc (s *S7_2) m7() string { //@ used(\"s\", true), used(\"m7\", true)\n\treturn \"\"\n}\n\ntype S7_3 struct{} //@ used(\"S7_3\", true)\nfunc (s *S7_3) m7() float32 { //@ quiet(\"s\"), used(\"m7\", false)\n\treturn 0\n}\n\ntype S7_4 struct{} //@ used(\"S7_4\", true)\nfunc (s *S7_4) m7() any { //@ quiet(\"s\"), used(\"m7\", false)\n\treturn nil\n}\n\ntype I8[T io.Reader] interface { //@ used(\"I8\", true), used(\"T\", true)\n\tm8() []T //@ used(\"m8\", true)\n}\n\ntype S8_1 struct{} //@ used(\"S8_1\", true)\n// This should be considered as used obviously. It's known incompleteness that we want to improve.\n// This test case just verifies that it doesn't crash.\nfunc (s *S8_1) m8() []io.Reader { //@ quiet(\"s\"), used(\"m8\", false)\n\treturn nil\n}\n\ntype S8 struct{}           //@ used(\"S8\", true)\ntype I9[T any] interface { //@ used(\"I9\", true), used(\"T\", true)\n\tmake() *T //@ used(\"make\", true)\n}\n\ntype S9 struct{} //@ used(\"S9\", true)\n\nfunc (S9) make() *S8 { return nil } //@ used(\"make\", true)\n\nfunc i9use(i I9[S8]) { i.make() } //@ used(\"i9use\", true), used(\"i\", true)\n\nfunc init() { //@ used(\"init\", true)\n\ti9use(S9{})\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/ignored/ignored.go",
    "content": "package pkg\n\n//lint:ignore U1000 consider yourself used\ntype t1 struct{} //@ used(\"t1\", true)\ntype t2 struct{} //@ used(\"t2\", true)\ntype t3 struct{} //@ used(\"t3\", true)\n\nfunc (t1) fn1() {} //@ used(\"fn1\", true)\nfunc (t1) fn2() {} //@ used(\"fn2\", true)\nfunc (t1) fn3() {} //@ used(\"fn3\", true)\n\n//lint:ignore U1000 be gone\nfunc (t2) fn1() {} //@ used(\"fn1\", true)\nfunc (t2) fn2() {} //@ used(\"fn2\", false)\nfunc (t2) fn3() {} //@ used(\"fn3\", false)\n\nfunc (t3) fn1() {} //@ used(\"fn1\", false)\nfunc (t3) fn2() {} //@ used(\"fn2\", false)\nfunc (t3) fn3() {} //@ used(\"fn3\", false)\n\n//lint:ignore U1000 consider yourself used\nfunc fn() { //@ used(\"fn\", true)\n\tvar _ t2 //@ used(\"_\", true)\n\tvar _ t3 //@ used(\"_\", true)\n}\n\n//lint:ignore U1000 bye\ntype t4 struct { //@ used(\"t4\", true)\n\tx int //@ used(\"x\", true)\n}\n\nfunc (t4) bar() {} //@ used(\"bar\", true)\n\n//lint:ignore U1000 consider yourself used\ntype t5 map[int]struct { //@ used(\"t5\", true)\n\ty int //@ used(\"y\", true)\n}\n\n//lint:ignore U1000 consider yourself used\ntype t6 interface { //@ used(\"t6\", true)\n\tfoo() //@ used(\"foo\", true)\n}\n\n//lint:ignore U1000 consider yourself used\ntype t7 = struct { //@ used(\"t7\", true)\n\tz int //@ used(\"z\", true)\n}\n\n//lint:ignore U1000 consider yourself used\ntype t8 struct{} //@ used(\"t8\", true)\n\nfunc (t8) fn() { //@ used(\"fn\", true)\n\totherFn()\n}\n\nfunc otherFn() {} //@ used(\"otherFn\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/ignored/ignored2.go",
    "content": "package pkg\n\nfunc (t1) fn4() {} //@ used(\"fn4\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/ignored/ignored3.go",
    "content": "//lint:file-ignore U1000 consider everything in here used\n\npackage pkg\n\ntype t9 struct{} //@ used(\"t9\", true)\n\nfunc (t9) fn1() {} //@ used(\"fn1\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/ignored/ignored4.go",
    "content": "package pkg\n\nfunc (t9) fn2() {} //@ used(\"fn2\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/implicit-conversion/implicit-conversion.go",
    "content": "package pkg\n\n// https://staticcheck.dev/issues/810\n\ntype Thing struct { //@ used(\"Thing\", true)\n\thas struct { //@ used(\"has\", true)\n\t\ta bool //@ used(\"a\", true)\n\t}\n}\n\nfunc Fn() { //@ used(\"Fn\", true)\n\ttype temp struct { //@ used(\"temp\", true)\n\t\ta bool //@ used(\"a\", true)\n\t}\n\n\tx := Thing{ //@ used(\"x\", true)\n\t\thas: temp{true},\n\t}\n\t_ = x\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/increment/increment.go",
    "content": "package pkg\n\ntype T struct { //@ used(\"T\", true), used_test(\"T\", true)\n\t// Writing to fields uses them\n\tf int //@ used(\"f\", true), used_test(\"f\", true)\n}\n\n// Not used, v is only written to\nvar v int //@ used(\"v\", false), used_test(\"v\", false)\n\nfunc Foo() { //@ used(\"Foo\", true), used_test(\"Foo\", true)\n\tvar x T //@ used(\"x\", true), used_test(\"x\", true)\n\tx.f++\n\tv++\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/increment/increment_test.go",
    "content": "package pkg\n\n// Used because v2 is a sink\nvar v2 int //@ used_test(\"v2\", true)\n\nfunc Bar() { //@ used_test(\"Bar\", true)\n\tv2++\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/index-write/write.go",
    "content": "package pkg\n\nvar x int //@ used(\"x\", true)\n\nfunc Foo() { //@ used(\"Foo\", true)\n\tvar s []int //@ used(\"s\", true)\n\ts[x] = 0\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/initializers/initializers.go",
    "content": "package pkg\n\n// https://staticcheck.dev/issues/507\n\nvar x = [3]int{1, 2, 3} //@ used(\"x\", false)\n"
  },
  {
    "path": "unused/testdata/src/example.com/instantiated-functions/instantiated-functions.go",
    "content": "package pkg\n\n// https://staticcheck.dev/issues/1199\n\ntype c1 struct{} //@ used(\"c1\", false)\n\nfunc Fn[T any]() {} //@ used(\"Fn\", true), used(\"T\", true)\n\nfunc uncalled() { //@ used(\"uncalled\", false)\n\tFn[c1]()\n}\n\ntype c2 struct{} //@ used(\"c2\", true)\n\nfunc Called() { //@ used(\"Called\", true)\n\tFn[c2]()\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/interfaces/interfaces.go",
    "content": "package pkg\n\ntype I interface { //@ used(\"I\", true)\n\tfn1() //@ used(\"fn1\", true)\n}\n\ntype t struct{} //@ used(\"t\", true)\n\nfunc (t) fn1() {} //@ used(\"fn1\", true)\nfunc (t) fn2() {} //@ used(\"fn2\", false)\n\nfunc init() { //@ used(\"init\", true)\n\t_ = t{}\n}\n\ntype I1 interface { //@ used(\"I1\", true)\n\tFoo() //@ used(\"Foo\", true)\n}\n\ntype I2 interface { //@ used(\"I2\", true)\n\tFoo() //@ used(\"Foo\", true)\n\tbar() //@ used(\"bar\", true)\n}\n\ntype i3 interface { //@ used(\"i3\", false)\n\tfoo() //@ quiet(\"foo\")\n\tbar() //@ quiet(\"bar\")\n}\n\ntype t1 struct{} //@ used(\"t1\", true)\ntype t2 struct{} //@ used(\"t2\", true)\ntype t3 struct{} //@ used(\"t3\", true)\ntype t4 struct { //@ used(\"t4\", true)\n\tt3 //@ used(\"t3\", true)\n}\n\nfunc (t1) Foo() {} //@ used(\"Foo\", true)\nfunc (t2) Foo() {} //@ used(\"Foo\", true)\nfunc (t2) bar() {} //@ used(\"bar\", true)\nfunc (t3) Foo() {} //@ used(\"Foo\", true)\nfunc (t3) bar() {} //@ used(\"bar\", true)\n\nfunc Fn() { //@ used(\"Fn\", true)\n\tvar v1 t1 //@ used(\"v1\", true)\n\tvar v2 t2 //@ used(\"v2\", true)\n\tvar v3 t3 //@ used(\"v3\", true)\n\tvar v4 t4 //@ used(\"v4\", true)\n\t_ = v1\n\t_ = v2\n\t_ = v3\n\tvar x interface{} = v4 //@ used(\"x\", true)\n\t_ = x.(I2)\n}\n\n// Text pointer receivers\ntype T2 struct{} //@ used(\"T2\", true)\n\nfunc (*T2) fn1() {} //@ used(\"fn1\", true)\nfunc (*T2) fn2() {} //@ used(\"fn2\", false)\n"
  },
  {
    "path": "unused/testdata/src/example.com/interfaces2/interfaces.go",
    "content": "package pkg\n\ntype I interface { //@ used(\"I\", true)\n\tfoo() //@ used(\"foo\", true)\n}\n\ntype T struct{} //@ used(\"T\", true)\n\nfunc (T) foo() {} //@ used(\"foo\", true)\nfunc (T) bar() {} //@ used(\"bar\", false)\n\nvar _ struct { //@ used(\"_\", true)\n\tT //@ used(\"T\", true)\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/issue1289/issue1289.go",
    "content": "package pkg\n\nfunc Fn1() { //@ used(\"Fn1\", true)\n\ttype Foo[T any] struct { //@ used(\"Foo\", true), used(\"T\", true)\n\t\tId   int `json:\"id\"`   //@ used(\"Id\", true)\n\t\tData T   `json:\"data\"` //@ used(\"Data\", true)\n\t}\n\ttype Bar struct { //@ used(\"Bar\", true)\n\t\tX int `json:\"x\"` //@ used(\"X\", true)\n\t\tY int `json:\"y\"` //@ used(\"Y\", true)\n\t}\n\tv := Foo[[]Bar]{} //@ used(\"v\", true)\n\t_ = v\n}\n\nfunc Fn2() { //@ used(\"Fn2\", true)\n\ttype Foo[T any] struct{} //@ used(\"Foo\", true), used(\"T\", true)\n\ttype Bar struct{}        //@ used(\"Bar\", true)\n\tv := Foo[[]Bar]{}        //@ used(\"v\", true)\n\t_ = v                    // just use it, but could be some json.Unmarshal, for instance\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/linkname/linkname.go",
    "content": "package pkg\n\nimport _ \"unsafe\"\n\n//other:directive\n//go:linkname ol other4\n\n//go:linkname foo other1\nfunc foo() {} //@ used(\"foo\", true)\n\n//go:linkname bar other2\nvar bar int //@ used(\"bar\", true)\n\nvar (\n\tbaz int //@ used(\"baz\", false)\n\t//go:linkname qux other3\n\tqux int //@ used(\"qux\", true)\n)\n\n//go:linkname fisk other3\nvar (\n\tfisk int //@ used(\"fisk\", true)\n)\n\nvar ol int //@ used(\"ol\", true)\n\n//go:linkname doesnotexist other5\n"
  },
  {
    "path": "unused/testdata/src/example.com/local-type-param-sink/typeparam.go",
    "content": "package tparamsource\n\n// https://staticcheck.dev/issues/1282\n\nimport \"reflect\"\n\nfunc TypeOfType[T any]() reflect.Type { //@ used(\"TypeOfType\", true), used(\"T\", true)\n\tvar t *T //@ used(\"t\", true)\n\treturn reflect.TypeOf(t).Elem()\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/local-type-param-source/typeparam.go",
    "content": "package tparamsource\n\n// https://staticcheck.dev/issues/1282\n\nimport (\n\t\"testing\"\n\n\ttparamsink \"example.com/local-type-param-sink\"\n)\n\nfunc TestFoo(t *testing.T) { //@ used(\"TestFoo\", true), used(\"t\", true)\n\ttype EmptyStruct struct{} //@ used(\"EmptyStruct\", true)\n\t_ = tparamsink.TypeOfType[EmptyStruct]()\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/main/main.go",
    "content": "package main\n\nfunc Fn1() {} //@ used(\"Fn1\", true)\nfunc Fn2() {} //@ used(\"Fn2\", true)\nfunc fn3() {} //@ used(\"fn3\", false)\n\nconst X = 1 //@ used(\"X\", true)\n\nvar Y = 2 //@ used(\"Y\", true)\n\ntype Z struct{} //@ used(\"Z\", true)\n\nfunc main() { //@ used(\"main\", true)\n\tFn1()\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/mapslice/mapslice.go",
    "content": "package pkg\n\ntype M map[int]int //@ used(\"M\", true)\n\nfunc Fn() { //@ used(\"Fn\", true)\n\tvar n M //@ used(\"n\", true)\n\t_ = []M{n}\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/methods/methods.go",
    "content": "package pkg\n\ntype t1 struct{} //@ used(\"t1\", true)\ntype t2 struct { //@ used(\"t2\", true)\n\tt3 //@ used(\"t3\", true)\n}\ntype t3 struct{} //@ used(\"t3\", true)\n\nfunc (t1) Foo() {} //@ used(\"Foo\", true)\nfunc (t3) Foo() {} //@ used(\"Foo\", true)\nfunc (t3) foo() {} //@ used(\"foo\", false)\n\nfunc init() { //@ used(\"init\", true)\n\t_ = t1{}\n\t_ = t2{}\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/named/named.go",
    "content": "package pkg\n\ntype t1 struct{} //@ used(\"t1\", true)\ntype T2 t1       //@ used(\"T2\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/nested/nested.go",
    "content": "package pkg\n\ntype t1 struct{} //@ used(\"t1\", false)\n\nfunc (t1) fragment() {} //@ used(\"fragment\", false)\n\nfunc fn1() bool { //@ used(\"fn1\", false)\n\tvar v interface{} = t1{} //@ quiet(\"v\")\n\tswitch obj := v.(type) { //@ quiet(\"obj\")\n\tcase interface {\n\t\tfragment() //@ quiet(\"fragment\")\n\t}:\n\t\tobj.fragment()\n\t}\n\treturn false\n}\n\ntype t2 struct{} //@ used(\"t2\", true)\n\nfunc (t2) fragment() {} //@ used(\"fragment\", true)\n\nfunc Fn() bool { //@ used(\"Fn\", true)\n\tvar v interface{} = t2{} //@ used(\"v\", true)\n\tswitch obj := v.(type) { //@ used(\"obj\", true)\n\tcase interface {\n\t\tfragment() //@ used(\"fragment\", true)\n\t}:\n\t\tobj.fragment()\n\t}\n\treturn false\n}\n\nfunc Fn2() bool { //@ used(\"Fn2\", true)\n\tvar v interface{} = t2{} //@ used(\"v\", true)\n\tswitch obj := v.(type) { //@ used(\"obj\", true)\n\tcase interface {\n\t\tfragment() //@ used(\"fragment\", true)\n\t}:\n\t\t_ = obj\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/nocopy/nocopy.go",
    "content": "package bar\n\ntype myNoCopy1 struct{}    //@ used(\"myNoCopy1\", true)\ntype myNoCopy2 struct{}    //@ used(\"myNoCopy2\", true)\ntype stdlibNoCopy struct{} //@ used(\"stdlibNoCopy\", true)\ntype locker struct{}       //@ used(\"locker\", false)\ntype someStruct struct {   //@ used(\"someStruct\", false)\n\tx int //@ quiet(\"x\")\n}\n\nfunc (myNoCopy1) Lock()      {} //@ used(\"Lock\", true)\nfunc (recv myNoCopy2) Lock() {} //@ used(\"Lock\", true), used(\"recv\", true)\nfunc (locker) Lock()         {} //@ used(\"Lock\", false)\nfunc (locker) Foobar()       {} //@ used(\"Foobar\", false)\nfunc (someStruct) Lock()     {} //@ used(\"Lock\", false)\n\nfunc (stdlibNoCopy) Lock()   {} //@ used(\"Lock\", true)\nfunc (stdlibNoCopy) Unlock() {} //@ used(\"Unlock\", true)\n\ntype T struct { //@ used(\"T\", true)\n\tnoCopy1 myNoCopy1    //@ used(\"noCopy1\", true)\n\tnoCopy2 myNoCopy2    //@ used(\"noCopy2\", true)\n\tnoCopy3 stdlibNoCopy //@ used(\"noCopy3\", true)\n\tfield1  someStruct   //@ used(\"field1\", false)\n\tfield2  locker       //@ used(\"field2\", false)\n\tfield3  int          //@ used(\"field3\", false)\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/nocopy-main/nocopy-main.go",
    "content": "package main\n\ntype myNoCopy1 struct{}   //@ used(\"myNoCopy1\", true)\ntype myNoCopy2 struct{}   //@ used(\"myNoCopy2\", true)\ntype wrongLocker struct{} //@ used(\"wrongLocker\", false)\ntype someStruct struct {  //@ used(\"someStruct\", false)\n\tx int //@ quiet(\"x\")\n}\n\nfunc (myNoCopy1) Lock()      {} //@ used(\"Lock\", true)\nfunc (recv myNoCopy2) Lock() {} //@ used(\"Lock\", true), used(\"recv\", true)\nfunc (wrongLocker) lock()    {} //@ used(\"lock\", false)\nfunc (wrongLocker) unlock()  {} //@ used(\"unlock\", false)\nfunc (someStruct) Lock()     {} //@ used(\"Lock\", false)\n\ntype T struct { //@ used(\"T\", true)\n\tnoCopy1 myNoCopy1   //@ used(\"noCopy1\", true)\n\tnoCopy2 myNoCopy2   //@ used(\"noCopy2\", true)\n\tfield1  someStruct  //@ used(\"field1\", false)\n\tfield2  wrongLocker //@ used(\"field2\", false)\n\tfield3  int         //@ used(\"field3\", false)\n}\n\nfunc main() { //@ used(\"main\", true)\n\t_ = T{}\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/nocopy-main/stub.go",
    "content": "package main\n"
  },
  {
    "path": "unused/testdata/src/example.com/parens/parens.go",
    "content": "package p\n\nfunc F(c chan bool) { //@ used(\"F\", true), used(\"c\", true)\n\tselect {\n\tcase (<-c):\n\tcase _ = (<-c):\n\t}\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/pointer-type-embedding/pointer-type-embedding.go",
    "content": "package pkg\n\nfunc init() { //@ used(\"init\", true)\n\tvar p P //@ used(\"p\", true)\n\t_ = p.n\n}\n\ntype T0 struct { //@ used(\"T0\", true)\n\tm int //@ used(\"m\", false)\n\tn int //@ used(\"n\", true)\n}\n\ntype T1 struct { //@ used(\"T1\", true)\n\tT0 //@ used(\"T0\", true)\n}\n\ntype P *T1 //@ used(\"P\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/pointers/pointers.go",
    "content": "package baz\n\nimport \"fmt\"\n\ntype Foo interface { //@ used(\"Foo\", true)\n\tbar() //@ used(\"bar\", true)\n}\n\nfunc Bar(f Foo) { //@ used(\"Bar\", true), used(\"f\", true)\n\tf.bar()\n}\n\ntype Buzz struct{} //@ used(\"Buzz\", true)\n\nfunc (b *Buzz) bar() { //@ used(\"bar\", true), used(\"b\", true)\n\tfmt.Println(\"foo bar buzz\")\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/quiet/quiet.go",
    "content": "package pkg\n\ntype iface interface { //@ used(\"iface\", false)\n\tfoo() //@ quiet(\"foo\")\n}\n\ntype t1 struct{} //@ used(\"t1\", false)\nfunc (t1) foo()  {} //@ used(\"foo\", false)\n\ntype t2 struct{} //@ used(\"t2\", true)\n\nfunc (t t2) bar(arg int) (ret int) { //@ quiet(\"t\"), used(\"bar\", false), quiet(\"arg\"), quiet(\"ret\")\n\treturn 0\n}\n\nfunc init() { //@ used(\"init\", true)\n\t_ = t2{}\n}\n\ntype t3 struct { //@ used(\"t3\", false)\n\ta int //@ quiet(\"a\")\n\tb int //@ quiet(\"b\")\n}\n\ntype T struct{} //@ used(\"T\", true)\n\nfunc fn1() { //@ used(\"fn1\", false)\n\tmeh := func(arg T) { //@ quiet(\"meh\"), quiet(\"arg\")\n\t}\n\tmeh(T{})\n}\n\ntype localityList []int //@ used(\"localityList\", false)\n\nfunc (l *localityList) Fn1() {} //@ quiet(\"l\"), used(\"Fn1\", false)\nfunc (l *localityList) Fn2() {} //@ quiet(\"l\"), used(\"Fn2\", false)\n"
  },
  {
    "path": "unused/testdata/src/example.com/selectors/selectors.go",
    "content": "package pkg\n\ntype t struct { //@ used(\"t\", true)\n\tf int //@ used(\"f\", true)\n}\n\nfunc fn(v *t) { //@ used(\"fn\", true), used(\"v\", true)\n\tprintln(v.f)\n}\n\nfunc init() { //@ used(\"init\", true)\n\tvar v t //@ used(\"v\", true)\n\tfn(&v)\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/skipped-test/skipped_test.go",
    "content": "package pkg\n\n// https://staticcheck.dev/issues/633\n\nimport \"testing\"\n\nfunc test() { //@ used_test(\"test\", true)\n}\n\nfunc TestSum(t *testing.T) { //@ used_test(\"TestSum\", true), used_test(\"t\", true)\n\tt.Skip(\"skipping for test\")\n\ttest()\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/switch_interface/switch_interface.go",
    "content": "package pkg\n\ntype t struct{} //@ used(\"t\", true)\n\nfunc (t) fragment() {} //@ used(\"fragment\", true)\n\nfunc fn() bool { //@ used(\"fn\", true)\n\tvar v interface{} = t{}  //@ used(\"v\", true)\n\tswitch obj := v.(type) { //@ used(\"obj\", true)\n\tcase interface {\n\t\tfragment() //@ used(\"fragment\", true)\n\t}:\n\t\tobj.fragment()\n\t}\n\treturn false\n}\n\nvar x = fn() //@ used(\"x\", true)\nvar _ = x    //@ used(\"_\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/tests/tests.go",
    "content": "package pkg\n\nfunc fn() {} //@ used(\"fn\", false), used_test(\"fn\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/tests/tests_test.go",
    "content": "package pkg\n\nimport \"testing\"\n\nfunc TestFn(t *testing.T) { //@ used_test(\"TestFn\", true), used_test(\"t\", true)\n\tfn()\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/tests-main/main.go",
    "content": "package main\n"
  },
  {
    "path": "unused/testdata/src/example.com/tests-main/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\ntype t1 struct{} //@ used_test(\"t1\", true)\n\nfunc TestFoo(t *testing.T) { //@ used_test(\"TestFoo\", true), used_test(\"t\", true)\n\t_ = t1{}\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/type-dedup/dedup.go",
    "content": "package pkg\n\ntype t1 struct { //@ used(\"t1\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", false)\n}\n\ntype t2 struct { //@ used(\"t2\", true)\n\ta int //@ used(\"a\", false)\n\tb int //@ used(\"b\", true)\n}\n\nfunc Fn() { //@ used(\"Fn\", true)\n\tx := t1{} //@ used(\"x\", true)\n\ty := t2{} //@ used(\"y\", true)\n\tprintln(x.a)\n\tprintln(y.b)\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/type-dedup2/dedup.go",
    "content": "package pkg\n\nfunc fn1(t struct { //@ used(\"fn1\", true), used(\"t\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n}) {\n\tprintln(t.a)\n\tfn2(t)\n}\n\nfunc fn2(t struct { //@ used(\"fn2\", true), used(\"t\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n}) {\n\tprintln(t.b)\n}\n\nfunc Fn() { //@ used(\"Fn\", true)\n\tfn1(struct {\n\t\ta int //@ used(\"a\", true)\n\t\tb int //@ used(\"b\", true)\n\t}{})\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/type-dedup3/dedup.go",
    "content": "package pkg\n\nfunc fn1(t struct { //@ used(\"fn1\", true), used(\"t\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n}) {\n\tfn2(t)\n}\n\nfunc fn2(t struct { //@ used(\"fn2\", true), used(\"t\", true)\n\ta int //@ used(\"a\", true)\n\tb int //@ used(\"b\", true)\n}) {\n\tprintln(t.a)\n\tprintln(t.b)\n}\n\nfunc Fn() { //@ used(\"Fn\", true)\n\tfn1(struct {\n\t\ta int //@ used(\"a\", true)\n\t\tb int //@ used(\"b\", true)\n\t}{1, 2})\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/typeparams/typeparams.go",
    "content": "//go:build go1.18\n\npackage pkg\n\ntype c1 struct{} //@ used(\"c1\", true)\ntype c2 struct{} //@ used(\"c2\", true)\ntype c3 struct{} //@ used(\"c3\", true)\ntype c4 struct{} //@ used(\"c4\", true)\ntype c5 struct{} //@ used(\"c5\", true)\ntype c6 struct{} //@ used(\"c6\", true)\ntype c7 struct{} //@ used(\"c7\", true)\ntype c8 struct{} //@ used(\"c8\", false)\ntype c9 struct{} //@ used(\"c9\", true)\n\ntype S1[T c1] struct{}  //@ used(\"S1\", true), used(\"T\", true)\ntype S2[T any] struct{} //@ used(\"S2\", true), used(\"T\", true)\ntype S3 S2[c2]          //@ used(\"S3\", true)\n\ntype I interface { //@ used(\"I\", true)\n\tc3 | c9\n}\n\nfunc Fn1[T c4]()  {} //@ used(\"Fn1\", true), used(\"T\", true)\nfunc fn2[T any]() {} //@ used(\"fn2\", true), used(\"T\", true)\nfunc Fn5[T any]() {} //@ used(\"Fn5\", true), used(\"T\", true)\nfunc Fn6[T any]() {} //@ used(\"Fn6\", true), used(\"T\", true)\n\nvar _ = fn2[c5] //@ used(\"_\", true)\n\nfunc Fn3() { //@ used(\"Fn3\", true)\n\tFn5[c6]()\n\t_ = S2[c7]{}\n}\n\nfunc uncalled() { //@ used(\"uncalled\", false)\n\t_ = Fn6[c8]\n}\n\ntype S4[T any] struct{} //@ used(\"S4\", true), used(\"T\", true)\n\nfunc (S4[T]) usedGenerically()  {} //@ used(\"usedGenerically\", true), used(\"T\", true)\nfunc (S4[T]) usedInstantiated() {} //@ used(\"usedInstantiated\", true), used(\"T\", true)\nfunc (recv S4[T]) Exported() { //@ used(\"Exported\", true), used(\"recv\", true), used(\"T\", true)\n\trecv.usedGenerically()\n}\nfunc (S4[T]) unused() {} //@ used(\"unused\", false), quiet(\"T\")\n\nfunc Fn4() { //@ used(\"Fn4\", true)\n\tvar x S4[int] //@ used(\"x\", true)\n\tx.usedInstantiated()\n}\n\ntype s1[T any] struct{} //@ used(\"s1\", false), quiet(\"T\")\n\nfunc (recv s1[a]) foo() { recv.foo(); recv.bar(); recv.baz() } //@ used(\"foo\", false), quiet(\"recv\"), quiet(\"a\")\nfunc (recv s1[b]) bar() { recv.foo(); recv.bar(); recv.baz() } //@ used(\"bar\", false), quiet(\"recv\"), quiet(\"b\")\nfunc (recv s1[c]) baz() { recv.foo(); recv.bar(); recv.baz() } //@ used(\"baz\", false), quiet(\"recv\"), quiet(\"c\")\n\nfunc fn7[T interface { //@ used(\"fn7\", false), quiet(\"T\")\n\tfoo() //@ quiet(\"foo\")\n}]() {\n}\n\nfunc fn8[T struct { //@ used(\"fn8\", false), quiet(\"T\")\n\tx int //@ quiet(\"x\")\n}]() {\n}\n\nfunc Fn9[T struct { //@ used(\"Fn9\", true), used(\"T\", true)\n\tX *s2 //@ used(\"X\", true)\n}]() {\n}\n\ntype s2 struct{} //@ used(\"s2\", true)\n\nfunc fn10[E any](x []E) {} //@ used(\"fn10\", false), quiet(\"E\"), quiet(\"x\")\n\ntype Tree[T any] struct { //@ used(\"Tree\", true), used(\"T\", true)\n\tRoot *Node[T] //@ used(\"Root\", true)\n}\n\ntype Node[T any] struct { //@ used(\"Node\", true), used(\"T\", true)\n\tTree *Tree[T] //@ used(\"Tree\", true)\n}\n\ntype foo struct{} //@ used(\"foo\", true)\n\ntype Bar *Node[foo] //@ used(\"Bar\", true)\n\nfunc (n Node[T]) anyMethod() {} //@ used(\"anyMethod\", false), quiet(\"n\"), quiet(\"T\")\n\nfunc fn11[T ~struct { //@ used(\"fn11\", false), quiet(\"T\")\n\tField int //@ quiet(\"Field\")\n}]() {\n\t// don't crash because of the composite literal\n\t_ = T{Field: 42}\n}\n\ntype convertGeneric1 struct { //@ used(\"convertGeneric1\", true)\n\tfield int //@ used(\"field\", true)\n}\n\ntype convertGeneric2 struct { //@ used(\"convertGeneric2\", true)\n\tfield int //@ used(\"field\", true)\n}\n\n// mark field as used\nvar _ = convertGeneric1{}.field //@ used(\"_\", true)\n\nfunc Fn12[T1 convertGeneric1, T2 convertGeneric2](a T1) { //@ used(\"Fn12\", true), used(\"T1\", true), used(\"T2\", true), used(\"a\", true)\n\t_ = T2(a) // conversion marks T2.field as used\n}\n\ntype S5[A, B any] struct{} //@ used(\"S5\", true), used(\"A\", true), used(\"B\", true)\ntype S6 S5[int, string]    //@ used(\"S6\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/typeparams/typeparams_17.go",
    "content": "//go:build !go1.18\n\npackage pkg\n"
  },
  {
    "path": "unused/testdata/src/example.com/types/types.go",
    "content": "package pkg\n\nimport \"reflect\"\n\ntype wkt interface { //@ used(\"wkt\", true)\n\tXXX_WellKnownType() string //@ used(\"XXX_WellKnownType\", true)\n}\n\nvar typeOfWkt = reflect.TypeOf((*wkt)(nil)).Elem() //@ used(\"typeOfWkt\", true)\n\nfunc Fn() { //@ used(\"Fn\", true)\n\t_ = typeOfWkt\n}\n\ntype t *int //@ used(\"t\", true)\n\nvar _ t //@ used(\"_\", true)\n"
  },
  {
    "path": "unused/testdata/src/example.com/unsafe-recursive/conversion.go",
    "content": "package pkg\n\n// https://staticcheck.dev/issues/1249\n\nimport \"unsafe\"\n\ntype t1 struct { //@ used(\"t1\", true)\n\tf1  int      //@ used(\"f1\", true)\n\tf2  t2       //@ used(\"f2\", true)\n\tf3  *t3      //@ used(\"f3\", true)\n\tt4           //@ used(\"t4\", true)\n\t*t5          //@ used(\"t5\", true)\n\tf6  [5]t6    //@ used(\"f6\", true)\n\tf7  [5][5]t7 //@ used(\"f7\", true)\n\tf8  nt1      //@ used(\"f8\", true)\n\tf9  nt2      //@ used(\"f9\", true)\n}\n\ntype nt1 *t8   //@ used(\"nt1\", true)\ntype nt2 [4]t9 //@ used(\"nt2\", true)\n\ntype t2 struct{ f int } //@ used(\"t2\", true), used(\"f\", true)\ntype t3 struct{ f int } //@ used(\"t3\", true), used(\"f\", false)\ntype t4 struct{ f int } //@ used(\"t4\", true), used(\"f\", true)\ntype t5 struct{ f int } //@ used(\"t5\", true), used(\"f\", false)\ntype t6 struct{ f int } //@ used(\"t6\", true), used(\"f\", true)\ntype t7 struct{ f int } //@ used(\"t7\", true), used(\"f\", true)\ntype t8 struct{ f int } //@ used(\"t8\", true), used(\"f\", false)\ntype t9 struct{ f int } //@ used(\"t9\", true), used(\"f\", true)\n\nfunc Foo(x t1) { //@ used(\"Foo\", true), used(\"x\", true)\n\t_ = unsafe.Pointer(&x)\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/unused-argument/unused-argument.go",
    "content": "package main\n\ntype t1 struct{} //@ used(\"t1\", true)\ntype t2 struct{} //@ used(\"t2\", true)\n\nfunc (t1) foo(arg *t2) {} //@ used(\"foo\", true), used(\"arg\", true)\n\nfunc init() { //@ used(\"init\", true)\n\tt1{}.foo(nil)\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/unused_type/unused_type.go",
    "content": "package pkg\n\ntype t1 struct{} //@ used(\"t1\", false)\n\nfunc (t1) Fn() {} //@ used(\"Fn\", false)\n\ntype t2 struct{} //@ used(\"t2\", true)\n\nfunc (*t2) Fn() {} //@ used(\"Fn\", true)\n\nfunc init() { //@ used(\"init\", true)\n\t(*t2).Fn(nil)\n}\n\ntype t3 struct{} //@ used(\"t3\", false)\n\nfunc (t3) fn() //@ used(\"fn\", false)\n"
  },
  {
    "path": "unused/testdata/src/example.com/variables/variables.go",
    "content": "package pkg\n\nvar a byte     //@ used(\"a\", true)\nvar b [16]byte //@ used(\"b\", true)\n\ntype t1 struct{} //@ used(\"t1\", true)\ntype t2 struct{} //@ used(\"t2\", true)\ntype t3 struct{} //@ used(\"t3\", true)\ntype t4 struct{} //@ used(\"t4\", true)\ntype t5 struct{} //@ used(\"t5\", true)\n\ntype iface interface{} //@ used(\"iface\", true)\n\nvar x t1           //@ used(\"x\", true)\nvar y = t2{}       //@ used(\"y\", true)\nvar j = t3{}       //@ used(\"j\", true)\nvar k = t4{}       //@ used(\"k\", true)\nvar l iface = t5{} //@ used(\"l\", true)\n\nfunc Fn() { //@ used(\"Fn\", true)\n\tprintln(a)\n\t_ = b[:]\n\n\t_ = x\n\t_ = y\n\t_ = j\n\t_ = k\n\t_ = l\n}\n"
  },
  {
    "path": "unused/testdata/src/example.com/variables/vartype.go",
    "content": "package pkg\n\ntype t181025 struct{} //@ used(\"t181025\", true)\n\nfunc (t181025) F() {} //@ used(\"F\", true)\n\n// package-level variable after function declaration used to trigger a\n// bug in unused.\n\nvar V181025 t181025 //@ used(\"V181025\", true)\n"
  },
  {
    "path": "unused/unused.go",
    "content": "// Package unused contains code for finding unused code.\npackage unused\n\nimport (\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"io\"\n\t\"reflect\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/facts/directives\"\n\t\"honnef.co/go/tools/analysis/facts/generated\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/analysis/report\"\n\t\"honnef.co/go/tools/go/ast/astutil\"\n\t\"honnef.co/go/tools/go/types/typeutil\"\n\n\t\"golang.org/x/tools/go/analysis\"\n\t\"golang.org/x/tools/go/types/objectpath\"\n)\n\n// OPT(dh): don't track local variables that can't have any interesting outgoing edges. For example, using a local\n// variable of type int is meaningless; we don't care if `int` is used or not.\n//\n// Note that we do have to track variables with for example array types, because the array type could have involved a\n// named constant.\n//\n// We probably have different culling needs depending on the mode of operation, too. If we analyze multiple packages in\n// one graph (unused's \"whole program\" mode), we could remove further useless edges (e.g. into nodes that themselves\n// have no outgoing edges and aren't meaningful objects on their own) after having analyzed a package, to keep the\n// in-memory representation small on average. If we only analyze a single package, that step would just waste cycles, as\n// we're about to throw the entire graph away, anyway.\n\n// TODO(dh): currently, types use methods that implement interfaces. However, this makes a method used even if the\n// relevant interface is never used. What if instead interfaces used those methods? Right now we cannot do that, because\n// methods use their receivers, so using a method uses the type. But do we need that edge? Is there a way to refer to a\n// method without explicitly mentioning the type somewhere? If not, the edge from method to receiver is superfluous.\n\n// XXX vet all code for proper use of core types\n\n// TODO(dh): we cannot observe function calls in assembly files.\n\n/*\n\nThis overview is true when using the default options. Different options may change individual behaviors.\n\n- packages use:\n  - (1.1) exported named types\n  - (1.2) exported functions (but not methods!)\n  - (1.3) exported variables\n  - (1.4) exported constants\n  - (1.5) init functions\n  - (1.6) functions exported to cgo\n  - (1.7) the main function iff in the main package\n  - (1.8) symbols linked via go:linkname\n  - (1.9) objects in generated files\n\n- named types use:\n  - (2.1) exported methods\n  - (2.2) the type they're based on\n  - (2.5) all their type parameters. Unused type parameters are probably useless, but they're a brand new feature and we\n    don't want to introduce false positives because we couldn't anticipate some novel use-case.\n  - (2.6) all their type arguments\n\n- functions use:\n  - (4.1) all their arguments, return parameters and receivers\n  - (4.2) anonymous functions defined beneath them\n  - (4.3) closures and bound methods.\n    this implements a simplified model where a function is used merely by being referenced, even if it is never called.\n    that way we don't have to keep track of closures escaping functions.\n  - (4.4) functions they return. we assume that someone else will call the returned function\n  - (4.5) functions/interface methods they call\n  - (4.6) types they instantiate or convert to\n  - (4.7) fields they access\n  - (4.9) package-level variables they assign to iff in tests (sinks for benchmarks)\n  - (4.10) all their type parameters. See 2.5 for reasoning.\n  - (4.11) local variables\n  - Note that the majority of this is handled implicitly by seeing idents be used. In particular, unlike the old\n    IR-based implementation, the AST-based one doesn't care about closures, bound methods or anonymous functions.\n    They're all just additional nodes in the AST.\n\n- conversions use:\n  - (5.1) when converting between two equivalent structs, the fields in\n    either struct use each other. the fields are relevant for the\n    conversion, but only if the fields are also accessed outside the\n    conversion.\n  - (5.2) when converting to or from unsafe.Pointer, mark all fields as used.\n\n- structs use:\n  - (6.1) fields of type NoCopy sentinel\n  - (6.2) exported fields\n  - (6.3) embedded fields that help implement interfaces (either fully implements it, or contributes required methods) (recursively)\n  - (6.4) embedded fields that have exported methods (recursively)\n  - (6.5) embedded structs that have exported fields (recursively)\n  - (6.6) all fields if they have a structs.HostLayout field\n\n- (7.1) field accesses use fields\n- (7.2) fields use their types\n\n- (8.0) How we handle interfaces:\n  - (8.1) We do not technically care about interfaces that only consist of\n    exported methods. Exported methods on concrete types are always\n    marked as used.\n  - (8.2) Any concrete type implements all known interfaces. Even if it isn't\n    assigned to any interfaces in our code, the user may receive a value\n    of the type and expect to pass it back to us through an interface.\n\n    Concrete types use their methods that implement interfaces. If the\n    type is used, it uses those methods. Otherwise, it doesn't. This\n    way, types aren't incorrectly marked reachable through the edge\n    from method to type.\n\n  - (8.3) All interface methods are marked as used, even if they never get\n    called. This is to accommodate sum types (unexported interface\n    method that must exist but never gets called.)\n\n  - (8.4) All embedded interfaces are marked as used. This is an\n    extension of 8.3, but we have to explicitly track embedded\n    interfaces because in a chain C->B->A, B wouldn't be marked as\n    used by 8.3 just because it contributes A's methods to C.\n\n- Inherent uses:\n  - (9.2) variables use their types\n  - (9.3) types use their underlying and element types\n  - (9.4) conversions use the type they convert to\n  - (9.7) variable _reads_ use variables, writes do not, except in tests\n  - (9.8) runtime functions that may be called from user code via the compiler\n  - (9.9) objects named the blank identifier are used. They cannot be referred to and are usually used explicitly to\n     use something that would otherwise be unused.\n  - The majority of idents get marked as read by virtue of being in the AST.\n\n- const groups:\n  - (10.1) if one constant out of a block of constants is used, mark all\n    of them used. a lot of the time, unused constants exist for the sake\n    of completeness. See also\n    https://github.com/dominikh/go-tools/issues/365\n\n    Do not, however, include constants named _ in constant groups.\n\n\n- (11.1) anonymous struct types use all their fields. we cannot\n  deduplicate struct types, as that leads to order-dependent\n  reports. we can't not deduplicate struct types while still\n  tracking fields, because then each instance of the unnamed type in\n  the data flow chain will get its own fields, causing false\n  positives. Thus, we only accurately track fields of named struct\n  types, and assume that unnamed struct types use all their fields.\n\n- type parameters use:\n  - (12.1) their constraint type\n\n*/\n\nvar Debug io.Writer\n\nfunc assert(b bool) {\n\tif !b {\n\t\tpanic(\"failed assertion\")\n\t}\n}\n\n// TODO(dh): should we return a map instead of two slices?\ntype Result struct {\n\tUsed   []Object\n\tUnused []Object\n\tQuiet  []Object\n}\n\nvar Analyzer = &lint.Analyzer{\n\tDoc: &lint.RawDocumentation{\n\t\tTitle: \"Unused code\",\n\t},\n\tAnalyzer: &analysis.Analyzer{\n\t\tName:       \"U1000\",\n\t\tDoc:        \"Unused code\",\n\t\tRun:        run,\n\t\tRequires:   []*analysis.Analyzer{generated.Analyzer, directives.Analyzer},\n\t\tResultType: reflect.TypeFor[Result](),\n\t},\n}\n\nfunc newGraph(\n\tfset *token.FileSet,\n\tfiles []*ast.File,\n\tpkg *types.Package,\n\tinfo *types.Info,\n\tdirectives []lint.Directive,\n\tgenerated map[string]generated.Generator,\n\topts Options,\n) *graph {\n\tg := graph{\n\t\tpkg:        pkg,\n\t\tinfo:       info,\n\t\tfiles:      files,\n\t\tdirectives: directives,\n\t\tgenerated:  generated,\n\t\tfset:       fset,\n\t\tnodes:      []Node{{}},\n\t\tedges:      map[edge]struct{}{},\n\t\tobjects:    map[types.Object]NodeID{},\n\t\topts:       opts,\n\t}\n\n\treturn &g\n}\n\nfunc run(pass *analysis.Pass) (any, error) {\n\tg := newGraph(\n\t\tpass.Fset,\n\t\tpass.Files,\n\t\tpass.Pkg,\n\t\tpass.TypesInfo,\n\t\tpass.ResultOf[directives.Analyzer].([]lint.Directive),\n\t\tpass.ResultOf[generated.Analyzer].(map[string]generated.Generator),\n\t\tDefaultOptions,\n\t)\n\tg.entry()\n\n\tsg := &SerializedGraph{\n\t\tnodes: g.nodes,\n\t}\n\n\tif Debug != nil {\n\t\tDebug.Write([]byte(sg.Dot()))\n\t}\n\n\treturn sg.Results(), nil\n}\n\ntype Options struct {\n\tFieldWritesAreUses     bool\n\tPostStatementsAreReads bool\n\tExportedIsUsed         bool\n\tExportedFieldsAreUsed  bool\n\tParametersAreUsed      bool\n\tLocalVariablesAreUsed  bool\n\tGeneratedIsUsed        bool\n}\n\nvar DefaultOptions = Options{\n\tFieldWritesAreUses:     true,\n\tPostStatementsAreReads: false,\n\tExportedIsUsed:         true,\n\tExportedFieldsAreUsed:  true,\n\tParametersAreUsed:      true,\n\tLocalVariablesAreUsed:  true,\n\tGeneratedIsUsed:        true,\n}\n\ntype edgeKind uint8\n\nconst (\n\tedgeKindUse = iota + 1\n\tedgeKindOwn\n)\n\ntype edge struct {\n\tfrom, to NodeID\n\tkind     edgeKind\n}\n\ntype graph struct {\n\tpkg        *types.Package\n\tinfo       *types.Info\n\tfiles      []*ast.File\n\tfset       *token.FileSet\n\tdirectives []lint.Directive\n\tgenerated  map[string]generated.Generator\n\n\topts Options\n\n\t// edges tracks all edges between nodes (uses and owns relationships). This data is also present in the Node struct,\n\t// but there it can't be accessed in O(1) time. edges is used to deduplicate edges.\n\tedges   map[edge]struct{}\n\tnodes   []Node\n\tobjects map[types.Object]NodeID\n\n\t// package-level named types\n\tnamedTypes     []*types.TypeName\n\tinterfaceTypes []*types.Interface\n}\n\ntype nodeState uint8\n\n//gcassert:inline\nfunc (ns nodeState) seen() bool { return ns&nodeStateSeen != 0 }\n\n//gcassert:inline\nfunc (ns nodeState) quiet() bool { return ns&nodeStateQuiet != 0 }\n\nconst (\n\tnodeStateSeen nodeState = 1 << iota\n\tnodeStateQuiet\n)\n\n// OPT(dh): 32 bits would be plenty, but the Node struct would end up with padding, anyway.\ntype NodeID uint64\n\ntype Node struct {\n\tid  NodeID\n\tobj Object\n\n\t// using slices instead of maps here helps make merging of graphs simpler and more efficient, because we can rewrite\n\t// IDs in place instead of having to build new maps.\n\tuses []NodeID\n\towns []NodeID\n}\n\nfunc (g *graph) objectToObject(obj types.Object) Object {\n\t// OPT(dh): I think we only need object paths in whole-program mode. In other cases, position-based node merging\n\t// should suffice.\n\n\t// objectpath.For is an expensive function and we'd like to avoid calling it when we know that there cannot be a\n\t// path, or when the path doesn't matter.\n\t//\n\t// Unexported global objects don't have paths. Local variables may have paths when they're parameters or return\n\t// parameters, but we do not care about those, because they're not API that other packages can refer to directly. We\n\t// do have to track fields, because they may be part of an anonymous type declared in a parameter or return\n\t// parameter. We cannot categorically ignore unexported identifiers, because an exported field might have been\n\t// embedded via an unexported field, which will be referred to.\n\n\tvar relevant bool\n\tswitch obj := obj.(type) {\n\tcase *types.Var:\n\t\t// If it's a field or it's an exported top-level variable, we care about it. Otherwise, we don't.\n\t\t// OPT(dh): same question as posed in the default branch\n\t\trelevant = obj.IsField() || token.IsExported(obj.Name())\n\tdefault:\n\t\t// OPT(dh): See if it's worth checking that the object is actually in package scope, and doesn't just have a\n\t\t// capitalized name.\n\t\trelevant = token.IsExported(obj.Name())\n\t}\n\n\tvar path ObjectPath\n\tif relevant {\n\t\tobjPath, _ := objectpath.For(obj)\n\t\tif objPath != \"\" {\n\t\t\tpath = ObjectPath{\n\t\t\t\tPkgPath: obj.Pkg().Path(),\n\t\t\t\tObjPath: objPath,\n\t\t\t}\n\t\t}\n\t}\n\tname := obj.Name()\n\tif sig, ok := obj.Type().(*types.Signature); ok && sig.Recv() != nil {\n\t\tswitch types.Unalias(sig.Recv().Type()).(type) {\n\t\tcase *types.Named, *types.Pointer:\n\t\t\ttyp := types.TypeString(sig.Recv().Type(), func(*types.Package) string { return \"\" })\n\t\t\tif len(typ) > 0 && typ[0] == '*' {\n\t\t\t\tname = fmt.Sprintf(\"(%s).%s\", typ, obj.Name())\n\t\t\t} else if len(typ) > 0 {\n\t\t\t\tname = fmt.Sprintf(\"%s.%s\", typ, obj.Name())\n\t\t\t}\n\t\t}\n\t}\n\treturn Object{\n\t\tName:            name,\n\t\tShortName:       obj.Name(),\n\t\tKind:            typString(obj),\n\t\tPath:            path,\n\t\tPosition:        g.fset.PositionFor(obj.Pos(), false),\n\t\tDisplayPosition: report.DisplayPosition(g.fset, obj.Pos()),\n\t}\n}\n\nfunc typString(obj types.Object) string {\n\tswitch obj := obj.(type) {\n\tcase *types.Func:\n\t\treturn \"func\"\n\tcase *types.Var:\n\t\tif obj.IsField() {\n\t\t\treturn \"field\"\n\t\t}\n\t\treturn \"var\"\n\tcase *types.Const:\n\t\treturn \"const\"\n\tcase *types.TypeName:\n\t\tif _, ok := obj.Type().(*types.TypeParam); ok {\n\t\t\treturn \"type param\"\n\t\t} else {\n\t\t\treturn \"type\"\n\t\t}\n\tdefault:\n\t\treturn \"identifier\"\n\t}\n}\n\nfunc (g *graph) newNode(obj types.Object) NodeID {\n\tid := NodeID(len(g.nodes))\n\tn := Node{\n\t\tid:  id,\n\t\tobj: g.objectToObject(obj),\n\t}\n\tg.nodes = append(g.nodes, n)\n\tif _, ok := g.objects[obj]; ok {\n\t\tpanic(fmt.Sprintf(\"already had a node for %s\", obj))\n\t}\n\tg.objects[obj] = id\n\treturn id\n}\n\nfunc (g *graph) node(obj types.Object) NodeID {\n\tif obj == nil {\n\t\treturn 0\n\t}\n\tobj = origin(obj)\n\tif n, ok := g.objects[obj]; ok {\n\t\treturn n\n\t}\n\tn := g.newNode(obj)\n\treturn n\n}\n\nfunc origin(obj types.Object) types.Object {\n\tswitch obj := obj.(type) {\n\tcase *types.Var:\n\t\treturn obj.Origin()\n\tcase *types.Func:\n\t\treturn obj.Origin()\n\tdefault:\n\t\treturn obj\n\t}\n}\n\nfunc (g *graph) addEdge(e edge) bool {\n\tif _, ok := g.edges[e]; ok {\n\t\treturn false\n\t}\n\tg.edges[e] = struct{}{}\n\treturn true\n}\n\nfunc (g *graph) addOwned(owner, owned NodeID) {\n\te := edge{owner, owned, edgeKindOwn}\n\tif !g.addEdge(e) {\n\t\treturn\n\t}\n\tn := &g.nodes[owner]\n\tn.owns = append(n.owns, owned)\n}\n\nfunc (g *graph) addUse(by, used NodeID) {\n\te := edge{by, used, edgeKindUse}\n\tif !g.addEdge(e) {\n\t\treturn\n\t}\n\tnBy := &g.nodes[by]\n\tnBy.uses = append(nBy.uses, used)\n}\n\nfunc (g *graph) see(obj, owner types.Object) {\n\tif obj == nil {\n\t\tpanic(\"saw nil object\")\n\t}\n\n\tif g.opts.ExportedIsUsed && obj.Pkg() != g.pkg || obj.Pkg() == nil {\n\t\treturn\n\t}\n\n\tnObj := g.node(obj)\n\tif owner != nil {\n\t\tnOwner := g.node(owner)\n\t\tg.addOwned(nOwner, nObj)\n\t}\n}\n\nfunc isIrrelevant(obj types.Object) bool {\n\tswitch obj.(type) {\n\tcase *types.PkgName:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (g *graph) use(used, by types.Object) {\n\tif g.opts.ExportedIsUsed {\n\t\tif used.Pkg() != g.pkg || used.Pkg() == nil {\n\t\t\treturn\n\t\t}\n\t\tif by != nil && by.Pkg() != g.pkg {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif isIrrelevant(used) {\n\t\treturn\n\t}\n\n\tnUsed := g.node(used)\n\tnBy := g.node(by)\n\tg.addUse(nBy, nUsed)\n}\n\nfunc (g *graph) entry() {\n\tfor _, f := range g.files {\n\t\tfor _, cg := range f.Comments {\n\t\t\tfor _, c := range cg.List {\n\t\t\t\tif strings.HasPrefix(c.Text, \"//go:linkname \") {\n\t\t\t\t\t// FIXME(dh): we're looking at all comments. The\n\t\t\t\t\t// compiler only looks at comments in the\n\t\t\t\t\t// left-most column. The intention probably is to\n\t\t\t\t\t// only look at top-level comments.\n\n\t\t\t\t\t// (1.8) packages use symbols linked via go:linkname\n\t\t\t\t\tfields := strings.Fields(c.Text)\n\t\t\t\t\tif len(fields) == 3 {\n\t\t\t\t\t\tobj := g.pkg.Scope().Lookup(fields[1])\n\t\t\t\t\t\tif obj == nil {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tg.use(obj, nil)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, f := range g.files {\n\t\tfor _, decl := range f.Decls {\n\t\t\tg.decl(decl, nil)\n\t\t}\n\t}\n\n\tif g.opts.GeneratedIsUsed {\n\t\t// OPT(dh): depending on the options used, we do not need to track all objects. For example, if local variables\n\t\t// are always used, then it is enough to use their surrounding function.\n\t\tfor obj := range g.objects {\n\t\t\tpath := g.fset.PositionFor(obj.Pos(), false).Filename\n\t\t\tif _, ok := g.generated[path]; ok {\n\t\t\t\tg.use(obj, nil)\n\t\t\t}\n\t\t}\n\t}\n\n\t// We use a normal map instead of a typeutil.Map because we deduplicate\n\t// these on a best effort basis, as an optimization.\n\tallInterfaces := make(map[*types.Interface]struct{})\n\tfor _, typ := range g.interfaceTypes {\n\t\tallInterfaces[typ] = struct{}{}\n\t}\n\tfor _, ins := range g.info.Instances {\n\t\tif typ, ok := ins.Type.(*types.Named); ok && typ.Obj().Pkg() == g.pkg {\n\t\t\tif iface, ok := typ.Underlying().(*types.Interface); ok {\n\t\t\t\tallInterfaces[iface] = struct{}{}\n\t\t\t}\n\t\t}\n\t}\n\tprocessMethodSet := func(named *types.TypeName, ms *types.MethodSet) {\n\t\tif g.opts.ExportedIsUsed {\n\t\t\tfor m := range ms.Methods() {\n\t\t\t\tif token.IsExported(m.Obj().Name()) {\n\t\t\t\t\t// (2.1) named types use exported methods\n\t\t\t\t\t// (6.4) structs use embedded fields that have exported methods\n\t\t\t\t\t//\n\t\t\t\t\t// By reading the selection, we read all embedded fields that are part of the path\n\t\t\t\t\tg.readSelection(m, named)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif _, ok := named.Type().Underlying().(*types.Interface); !ok {\n\t\t\t// (8.0) handle interfaces\n\t\t\t//\n\t\t\t// We don't care about interfaces implementing interfaces; all their methods are already used, anyway\n\t\t\tfor iface := range allInterfaces {\n\t\t\t\tif sels, ok := implements(named.Type(), iface, ms); ok {\n\t\t\t\t\tfor _, sel := range sels {\n\t\t\t\t\t\t// (8.2) any concrete type implements all known interfaces\n\t\t\t\t\t\t// (6.3) structs use embedded fields that help implement interfaces\n\t\t\t\t\t\tg.readSelection(sel, named)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, named := range g.namedTypes {\n\t\t// OPT(dh): do we already have the method set available?\n\t\tprocessMethodSet(named, types.NewMethodSet(named.Type()))\n\t\tprocessMethodSet(named, types.NewMethodSet(types.NewPointer(named.Type())))\n\n\t}\n\n\ttype ignoredKey struct {\n\t\tfile string\n\t\tline int\n\t}\n\tignores := map[ignoredKey]struct{}{}\n\tfor _, dir := range g.directives {\n\t\tif dir.Command != \"ignore\" && dir.Command != \"file-ignore\" {\n\t\t\tcontinue\n\t\t}\n\t\tif len(dir.Arguments) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif slices.Contains(strings.Split(dir.Arguments[0], \",\"), \"U1000\") {\n\t\t\tpos := g.fset.PositionFor(dir.Node.Pos(), false)\n\t\t\tvar key ignoredKey\n\t\t\tswitch dir.Command {\n\t\t\tcase \"ignore\":\n\t\t\t\tkey = ignoredKey{\n\t\t\t\t\tpos.Filename,\n\t\t\t\t\tpos.Line,\n\t\t\t\t}\n\t\t\tcase \"file-ignore\":\n\t\t\t\tkey = ignoredKey{\n\t\t\t\t\tpos.Filename,\n\t\t\t\t\t-1,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tignores[key] = struct{}{}\n\t\t}\n\t}\n\n\tif len(ignores) > 0 {\n\t\t// all objects annotated with a //lint:ignore U1000 are considered used\n\t\tfor obj := range g.objects {\n\t\t\tpos := g.fset.PositionFor(obj.Pos(), false)\n\t\t\tkey1 := ignoredKey{\n\t\t\t\tpos.Filename,\n\t\t\t\tpos.Line,\n\t\t\t}\n\t\t\tkey2 := ignoredKey{\n\t\t\t\tpos.Filename,\n\t\t\t\t-1,\n\t\t\t}\n\t\t\t_, ok := ignores[key1]\n\t\t\tif !ok {\n\t\t\t\t_, ok = ignores[key2]\n\t\t\t}\n\t\t\tif ok {\n\t\t\t\tg.use(obj, nil)\n\n\t\t\t\t// use methods and fields of ignored types\n\t\t\t\tif obj, ok := obj.(*types.TypeName); ok {\n\t\t\t\t\tif obj.IsAlias() {\n\t\t\t\t\t\tif typ, ok := types.Unalias(obj.Type()).(*types.Named); ok && (g.opts.ExportedIsUsed && typ.Obj().Pkg() != obj.Pkg() || typ.Obj().Pkg() == nil) {\n\t\t\t\t\t\t\t// This is an alias of a named type in another package.\n\t\t\t\t\t\t\t// Don't walk its fields or methods; we don't have to.\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// For aliases to types in the same package, we do want to ignore the fields and methods,\n\t\t\t\t\t\t\t// because ignoring the alias should ignore the aliased type.\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif typ, ok := types.Unalias(obj.Type()).(*types.Named); ok {\n\t\t\t\t\t\tfor method := range typ.Methods() {\n\t\t\t\t\t\t\tg.use(method, nil)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif typ, ok := obj.Type().Underlying().(*types.Struct); ok {\n\t\t\t\t\t\tfor field := range typ.Fields() {\n\t\t\t\t\t\t\tg.use(field, nil)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc isOfType[T any](x any) bool {\n\t_, ok := x.(T)\n\treturn ok\n}\n\nfunc (g *graph) read(node ast.Node, by types.Object) {\n\tif node == nil {\n\t\treturn\n\t}\n\n\tswitch node := node.(type) {\n\tcase *ast.Ident:\n\t\t// Among many other things, this handles\n\t\t// (7.1) field accesses use fields\n\n\t\tobj := g.info.ObjectOf(node)\n\t\tg.use(obj, by)\n\n\tcase *ast.BasicLit:\n\t\t// Nothing to do\n\n\tcase *ast.SliceExpr:\n\t\tg.read(node.X, by)\n\t\tg.read(node.Low, by)\n\t\tg.read(node.High, by)\n\t\tg.read(node.Max, by)\n\n\tcase *ast.UnaryExpr:\n\t\tg.read(node.X, by)\n\n\tcase *ast.ParenExpr:\n\t\tg.read(node.X, by)\n\n\tcase *ast.ArrayType:\n\t\tg.read(node.Len, by)\n\t\tg.read(node.Elt, by)\n\n\tcase *ast.SelectorExpr:\n\t\tg.readSelectorExpr(node, by)\n\n\tcase *ast.IndexExpr:\n\t\t// Among many other things, this handles\n\t\t// (2.6) named types use all their type arguments\n\t\tg.read(node.X, by)\n\t\tg.read(node.Index, by)\n\n\tcase *ast.IndexListExpr:\n\t\t// Among many other things, this handles\n\t\t// (2.6) named types use all their type arguments\n\t\tg.read(node.X, by)\n\t\tfor _, index := range node.Indices {\n\t\t\tg.read(index, by)\n\t\t}\n\n\tcase *ast.BinaryExpr:\n\t\tg.read(node.X, by)\n\t\tg.read(node.Y, by)\n\n\tcase *ast.CompositeLit:\n\t\tg.read(node.Type, by)\n\t\t// We get the type of the node itself, not of node.Type, to handle nested composite literals of the kind\n\t\t// T{{...}}\n\t\ttyp, isStruct := typeutil.CoreType(g.info.TypeOf(node)).(*types.Struct)\n\n\t\tif isStruct {\n\t\t\tunkeyed := len(node.Elts) != 0 && !isOfType[*ast.KeyValueExpr](node.Elts[0])\n\t\t\tif g.opts.FieldWritesAreUses && unkeyed {\n\t\t\t\t// Untagged struct literal that specifies all fields. We have to manually use the fields in the type,\n\t\t\t\t// because the unkeyd literal doesn't contain any nodes referring to the fields.\n\t\t\t\tfor field := range typ.Fields() {\n\t\t\t\t\tg.use(field, by)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif g.opts.FieldWritesAreUses || unkeyed {\n\t\t\t\tfor _, elt := range node.Elts {\n\t\t\t\t\tg.read(elt, by)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor _, elt := range node.Elts {\n\t\t\t\t\tkv := elt.(*ast.KeyValueExpr)\n\t\t\t\t\tg.write(kv.Key, by)\n\t\t\t\t\tg.read(kv.Value, by)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor _, elt := range node.Elts {\n\t\t\t\tg.read(elt, by)\n\t\t\t}\n\t\t}\n\n\tcase *ast.KeyValueExpr:\n\t\tg.read(node.Key, by)\n\t\tg.read(node.Value, by)\n\n\tcase *ast.StarExpr:\n\t\tg.read(node.X, by)\n\n\tcase *ast.MapType:\n\t\tg.read(node.Key, by)\n\t\tg.read(node.Value, by)\n\n\tcase *ast.FuncLit:\n\t\tg.read(node.Type, by)\n\n\t\t// See graph.decl's handling of ast.FuncDecl for why this bit of code is necessary.\n\t\tfn := g.info.TypeOf(node).(*types.Signature)\n\t\tfor params, i := fn.Params(), 0; i < params.Len(); i++ {\n\t\t\tg.see(params.At(i), by)\n\t\t\tif params.At(i).Name() == \"\" {\n\t\t\t\tg.use(params.At(i), by)\n\t\t\t}\n\t\t}\n\n\t\tg.block(node.Body, by)\n\n\tcase *ast.FuncType:\n\t\tm := map[*types.Var]struct{}{}\n\t\tif !g.opts.ParametersAreUsed {\n\t\t\tm = map[*types.Var]struct{}{}\n\t\t\t// seeScope marks all local variables in the scope as used, but we don't want to unconditionally use\n\t\t\t// parameters, as this is controlled by Options.ParametersAreUsed. Pass seeScope a list of variables it\n\t\t\t// should skip.\n\t\t\tfor _, f := range node.Params.List {\n\t\t\t\tfor _, name := range f.Names {\n\t\t\t\t\tm[g.info.ObjectOf(name).(*types.Var)] = struct{}{}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tg.seeScope(node, by, m)\n\n\t\t// (4.1) functions use all their arguments, return parameters and receivers\n\t\t// (12.1) type parameters use their constraint type\n\t\tg.read(node.TypeParams, by)\n\t\tif g.opts.ParametersAreUsed {\n\t\t\tg.read(node.Params, by)\n\t\t}\n\t\tg.read(node.Results, by)\n\n\tcase *ast.FieldList:\n\t\tif node == nil {\n\t\t\treturn\n\t\t}\n\n\t\t// This branch is only hit for field lists enclosed by parentheses or square brackets, i.e. parameters. Fields\n\t\t// (for structs) and method lists (for interfaces) are handled elsewhere.\n\n\t\tfor _, field := range node.List {\n\t\t\tif len(field.Names) == 0 {\n\t\t\t\tg.read(field.Type, by)\n\t\t\t} else {\n\t\t\t\tfor _, name := range field.Names {\n\t\t\t\t\t// OPT(dh): instead of by -> name -> type, we could just emit by -> type. We don't care about the\n\t\t\t\t\t// (un)usedness of parameters of any kind.\n\t\t\t\t\tobj := g.info.ObjectOf(name)\n\t\t\t\t\tg.use(obj, by)\n\t\t\t\t\tg.read(field.Type, obj)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tcase *ast.ChanType:\n\t\tg.read(node.Value, by)\n\n\tcase *ast.StructType:\n\t\t// This is only used for anonymous struct types, not named ones.\n\n\t\tfor _, field := range node.Fields.List {\n\t\t\tif len(field.Names) == 0 {\n\t\t\t\t// embedded field\n\n\t\t\t\tf := g.embeddedField(field.Type, by)\n\t\t\t\tg.use(f, by)\n\t\t\t} else {\n\t\t\t\tfor _, name := range field.Names {\n\t\t\t\t\t// (11.1) anonymous struct types use all their fields\n\t\t\t\t\t// OPT(dh): instead of by -> name -> type, we could just emit by -> type. If the type is used, then the fields are used.\n\t\t\t\t\tobj := g.info.ObjectOf(name)\n\t\t\t\t\tg.see(obj, by)\n\t\t\t\t\tg.use(obj, by)\n\t\t\t\t\tg.read(field.Type, g.info.ObjectOf(name))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tcase *ast.TypeAssertExpr:\n\t\tg.read(node.X, by)\n\t\tg.read(node.Type, by)\n\n\tcase *ast.InterfaceType:\n\t\tif len(node.Methods.List) != 0 {\n\t\t\tg.interfaceTypes = append(g.interfaceTypes, g.info.TypeOf(node).(*types.Interface))\n\t\t}\n\t\tfor _, meth := range node.Methods.List {\n\t\t\tswitch len(meth.Names) {\n\t\t\tcase 0:\n\t\t\t\t// Embedded type or type union\n\t\t\t\t// (8.4) all embedded interfaces are marked as used\n\t\t\t\t// (this also covers type sets)\n\n\t\t\t\tg.read(meth.Type, by)\n\t\t\tcase 1:\n\t\t\t\t// Method\n\t\t\t\t// (8.3) all interface methods are marked as used\n\t\t\t\tobj := g.info.ObjectOf(meth.Names[0])\n\t\t\t\tg.see(obj, by)\n\t\t\t\tg.use(obj, by)\n\t\t\t\tg.read(meth.Type, obj)\n\t\t\tdefault:\n\t\t\t\tpanic(fmt.Sprintf(\"unexpected number of names: %d\", len(meth.Names)))\n\t\t\t}\n\t\t}\n\n\tcase *ast.Ellipsis:\n\t\tg.read(node.Elt, by)\n\n\tcase *ast.CallExpr:\n\t\tg.read(node.Fun, by)\n\t\tfor _, arg := range node.Args {\n\t\t\tg.read(arg, by)\n\t\t}\n\n\t\t// Handle conversions\n\t\tconv := node\n\t\tif len(conv.Args) != 1 || conv.Ellipsis.IsValid() {\n\t\t\treturn\n\t\t}\n\n\t\tdst := g.info.TypeOf(conv.Fun)\n\t\tsrc := g.info.TypeOf(conv.Args[0])\n\n\t\t// XXX use DereferenceR instead\n\t\t// XXX guard against infinite recursion in DereferenceR\n\t\ttSrc := typeutil.CoreType(typeutil.Dereference(src))\n\t\ttDst := typeutil.CoreType(typeutil.Dereference(dst))\n\t\tstSrc, okSrc := tSrc.(*types.Struct)\n\t\tstDst, okDst := tDst.(*types.Struct)\n\t\tif okDst && okSrc {\n\t\t\t// Converting between two structs. The fields are\n\t\t\t// relevant for the conversion, but only if the\n\t\t\t// fields are also used outside of the conversion.\n\t\t\t// Mark fields as used by each other.\n\n\t\t\tassert(stDst.NumFields() == stSrc.NumFields())\n\t\t\tfor i := 0; i < stDst.NumFields(); i++ {\n\t\t\t\t// (5.1) when converting between two equivalent structs, the fields in\n\t\t\t\t// either struct use each other. the fields are relevant for the\n\t\t\t\t// conversion, but only if the fields are also accessed outside the\n\t\t\t\t// conversion.\n\t\t\t\tg.use(stDst.Field(i), stSrc.Field(i))\n\t\t\t\tg.use(stSrc.Field(i), stDst.Field(i))\n\t\t\t}\n\t\t} else if okSrc && tDst == types.Typ[types.UnsafePointer] {\n\t\t\t// (5.2) when converting to or from unsafe.Pointer, mark all fields as used.\n\t\t\tg.useAllFieldsRecursively(stSrc, by)\n\t\t} else if okDst && tSrc == types.Typ[types.UnsafePointer] {\n\t\t\t// (5.2) when converting to or from unsafe.Pointer, mark all fields as used.\n\t\t\tg.useAllFieldsRecursively(stDst, by)\n\t\t}\n\n\tdefault:\n\t\tlint.ExhaustiveTypeSwitch(node)\n\t}\n}\n\nfunc (g *graph) useAllFieldsRecursively(typ types.Type, by types.Object) {\n\tswitch typ := typ.Underlying().(type) {\n\tcase *types.Struct:\n\t\tfor field := range typ.Fields() {\n\t\t\tg.use(field, by)\n\t\t\tg.useAllFieldsRecursively(field.Type(), by)\n\t\t}\n\tcase *types.Array:\n\t\tg.useAllFieldsRecursively(typ.Elem(), by)\n\tdefault:\n\t\treturn\n\t}\n}\n\nfunc (g *graph) write(node ast.Node, by types.Object) {\n\tif node == nil {\n\t\treturn\n\t}\n\n\tswitch node := node.(type) {\n\tcase *ast.Ident:\n\t\tobj := g.info.ObjectOf(node)\n\t\tif obj == nil {\n\t\t\t// This can happen for `switch x := v.(type)`, where that x doesn't have an object\n\t\t\treturn\n\t\t}\n\n\t\t// (4.9) functions use package-level variables they assign to iff in tests (sinks for benchmarks)\n\t\t// (9.7) variable _reads_ use variables, writes do not, except in tests\n\t\tpath := g.fset.File(obj.Pos()).Name()\n\t\tif strings.HasSuffix(path, \"_test.go\") {\n\t\t\tif isGlobal(obj) {\n\t\t\t\tg.use(obj, by)\n\t\t\t}\n\t\t}\n\n\tcase *ast.IndexExpr:\n\t\tg.read(node.X, by)\n\t\tg.read(node.Index, by)\n\n\tcase *ast.SelectorExpr:\n\t\tif g.opts.FieldWritesAreUses {\n\t\t\t// Writing to a field constitutes a use. See https://staticcheck.dev/issues/288 for some discussion on that.\n\t\t\t//\n\t\t\t// This code can also get triggered by qualified package variables, in which case it doesn't matter what we do,\n\t\t\t// because the object is in another package.\n\t\t\t//\n\t\t\t// FIXME(dh): ^ isn't true if we track usedness of exported identifiers\n\t\t\tg.readSelectorExpr(node, by)\n\t\t} else {\n\t\t\tg.read(node.X, by)\n\t\t\tg.write(node.Sel, by)\n\t\t}\n\n\tcase *ast.StarExpr:\n\t\tg.read(node.X, by)\n\n\tcase *ast.ParenExpr:\n\t\tg.write(node.X, by)\n\n\tdefault:\n\t\tlint.ExhaustiveTypeSwitch(node)\n\t}\n}\n\n// readSelectorExpr reads all elements of a selector expression, including implicit fields.\nfunc (g *graph) readSelectorExpr(sel *ast.SelectorExpr, by types.Object) {\n\t// cover AST-based accesses\n\tg.read(sel.X, by)\n\tg.read(sel.Sel, by)\n\n\ttsel, ok := g.info.Selections[sel]\n\tif !ok {\n\t\treturn\n\t}\n\tg.readSelection(tsel, by)\n}\n\nfunc (g *graph) readSelection(sel *types.Selection, by types.Object) {\n\tindices := sel.Index()\n\tbase := sel.Recv()\n\tfor _, idx := range indices[:len(indices)-1] {\n\t\t// XXX do we need core types here?\n\t\tfield := typeutil.Dereference(base.Underlying()).Underlying().(*types.Struct).Field(idx)\n\t\tg.use(field, by)\n\t\tbase = field.Type()\n\t}\n\n\tg.use(sel.Obj(), by)\n}\n\nfunc (g *graph) block(block *ast.BlockStmt, by types.Object) {\n\tif block == nil {\n\t\treturn\n\t}\n\n\tg.seeScope(block, by, nil)\n\tfor _, stmt := range block.List {\n\t\tg.stmt(stmt, by)\n\t}\n}\n\nfunc isGlobal(obj types.Object) bool {\n\treturn obj.Parent() == obj.Pkg().Scope()\n}\n\nfunc (g *graph) decl(decl ast.Decl, by types.Object) {\n\tswitch decl := decl.(type) {\n\tcase *ast.GenDecl:\n\t\tswitch decl.Tok {\n\t\tcase token.IMPORT:\n\t\t\t// Nothing to do\n\n\t\tcase token.CONST:\n\t\t\tfor _, spec := range decl.Specs {\n\t\t\t\tvspec := spec.(*ast.ValueSpec)\n\t\t\t\tassert(len(vspec.Values) == 0 || len(vspec.Values) == len(vspec.Names))\n\t\t\t\tfor i, name := range vspec.Names {\n\t\t\t\t\tobj := g.info.ObjectOf(name)\n\t\t\t\t\tg.see(obj, by)\n\t\t\t\t\tg.read(vspec.Type, obj)\n\n\t\t\t\t\tif len(vspec.Values) != 0 {\n\t\t\t\t\t\tg.read(vspec.Values[i], obj)\n\t\t\t\t\t}\n\n\t\t\t\t\tif name.Name == \"_\" {\n\t\t\t\t\t\t// (9.9) objects named the blank identifier are used\n\t\t\t\t\t\tg.use(obj, by)\n\t\t\t\t\t} else if token.IsExported(name.Name) && isGlobal(obj) && g.opts.ExportedIsUsed {\n\t\t\t\t\t\tg.use(obj, nil)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tgroups := astutil.GroupSpecs(g.fset, decl.Specs)\n\t\t\tfor _, group := range groups {\n\t\t\t\t// (10.1) if one constant out of a block of constants is used, mark all of them used\n\t\t\t\t//\n\t\t\t\t// We encode this as a ring. If we have a constant group 'const ( a; b; c )', then we'll produce the\n\t\t\t\t// following graph: a -> b -> c -> a.\n\n\t\t\t\tvar first, prev, last types.Object\n\t\t\t\tfor _, spec := range group {\n\t\t\t\t\tfor _, name := range spec.(*ast.ValueSpec).Names {\n\t\t\t\t\t\tif name.Name == \"_\" {\n\t\t\t\t\t\t\t// Having a blank constant in a group doesn't mark the whole group as used\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tobj := g.info.ObjectOf(name)\n\t\t\t\t\t\tif first == nil {\n\t\t\t\t\t\t\tfirst = obj\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tg.use(obj, prev)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprev = obj\n\t\t\t\t\t\tlast = obj\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif first != nil && first != last {\n\t\t\t\t\tg.use(first, last)\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase token.TYPE:\n\t\t\tfor _, spec := range decl.Specs {\n\t\t\t\ttspec := spec.(*ast.TypeSpec)\n\t\t\t\tobj := g.info.ObjectOf(tspec.Name).(*types.TypeName)\n\t\t\t\tg.see(obj, by)\n\t\t\t\tg.seeScope(tspec, obj, nil)\n\t\t\t\tif !tspec.Assign.IsValid() {\n\t\t\t\t\tg.namedTypes = append(g.namedTypes, obj)\n\t\t\t\t}\n\t\t\t\tif token.IsExported(tspec.Name.Name) && isGlobal(obj) && g.opts.ExportedIsUsed {\n\t\t\t\t\t// (1.1) packages use exported named types\n\t\t\t\t\tg.use(g.info.ObjectOf(tspec.Name), nil)\n\t\t\t\t}\n\n\t\t\t\t// (2.5) named types use all their type parameters\n\t\t\t\tg.read(tspec.TypeParams, obj)\n\n\t\t\t\tg.namedType(obj, tspec.Type)\n\n\t\t\t\tif tspec.Name.Name == \"_\" {\n\t\t\t\t\t// (9.9) objects named the blank identifier are used\n\t\t\t\t\tg.use(obj, by)\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase token.VAR:\n\t\t\t// We cannot rely on types.Initializer for package-level variables because\n\t\t\t// - initializers are only tracked for variables that are actually initialized\n\t\t\t// - we want to see the AST of the type, if specified, not just the rhs\n\n\t\t\tfor _, spec := range decl.Specs {\n\t\t\t\tvspec := spec.(*ast.ValueSpec)\n\t\t\t\tfor i, name := range vspec.Names {\n\t\t\t\t\tobj := g.info.ObjectOf(name)\n\t\t\t\t\tg.see(obj, by)\n\t\t\t\t\t// variables and constants use their types\n\t\t\t\t\tg.read(vspec.Type, obj)\n\n\t\t\t\t\tif len(vspec.Names) == len(vspec.Values) {\n\t\t\t\t\t\t// One value per variable\n\t\t\t\t\t\tg.read(vspec.Values[i], obj)\n\t\t\t\t\t} else if len(vspec.Values) != 0 {\n\t\t\t\t\t\t// Multiple variables initialized with a single rhs\n\t\t\t\t\t\t// assert(len(vspec.Values) == 1)\n\t\t\t\t\t\tif len(vspec.Values) != 1 {\n\t\t\t\t\t\t\tpanic(g.fset.PositionFor(vspec.Pos(), false))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tg.read(vspec.Values[0], obj)\n\t\t\t\t\t}\n\n\t\t\t\t\tif token.IsExported(name.Name) && isGlobal(obj) && g.opts.ExportedIsUsed {\n\t\t\t\t\t\t// (1.3) packages use exported variables\n\t\t\t\t\t\tg.use(obj, nil)\n\t\t\t\t\t}\n\n\t\t\t\t\tif name.Name == \"_\" {\n\t\t\t\t\t\t// (9.9) objects named the blank identifier are used\n\t\t\t\t\t\tg.use(obj, by)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"unexpected token %s\", decl.Tok))\n\t\t}\n\n\tcase *ast.FuncDecl:\n\t\tobj := g.info.ObjectOf(decl.Name).(*types.Func).Origin()\n\t\tg.see(obj, nil)\n\n\t\tif token.IsExported(decl.Name.Name) && g.opts.ExportedIsUsed {\n\t\t\tif decl.Recv == nil {\n\t\t\t\t// (1.2) packages use exported functions\n\t\t\t\tg.use(obj, nil)\n\t\t\t}\n\t\t} else if decl.Name.Name == \"init\" {\n\t\t\t// (1.5) packages use init functions\n\t\t\tg.use(obj, nil)\n\t\t} else if decl.Name.Name == \"main\" && g.pkg.Name() == \"main\" {\n\t\t\t// (1.7) packages use the main function iff in the main package\n\t\t\tg.use(obj, nil)\n\t\t} else if g.pkg.Path() == \"runtime\" && runtimeFuncs[decl.Name.Name] {\n\t\t\t// (9.8) runtime functions that may be called from user code via the compiler\n\t\t\tg.use(obj, nil)\n\t\t} else if g.pkg.Path() == \"runtime/coverage\" && runtimeCoverageFuncs[decl.Name.Name] {\n\t\t\t// (9.8) runtime functions that may be called from user code via the compiler\n\t\t\tg.use(obj, nil)\n\t\t}\n\n\t\t// (4.1) functions use their receivers\n\t\tg.read(decl.Recv, obj)\n\t\tg.read(decl.Type, obj)\n\t\tg.block(decl.Body, obj)\n\n\t\t// g.read(decl.Type) will ultimately call g.seeScopes and see parameters that way. But because it relies\n\t\t// entirely on the AST, it cannot resolve unnamed parameters to types.Object. For that reason we explicitly\n\t\t// handle arguments here, as well as for FuncLits elsewhere.\n\t\t//\n\t\t// g.seeScopes can't get to the types.Signature for this function because there is no mapping from ast.FuncType to\n\t\t// types.Signature, only from ast.Ident to types.Signature.\n\t\t//\n\t\t// This code is only really relevant when Options.ParametersAreUsed is false. Otherwise, all parameters are\n\t\t// considered used, and if we never see a parameter then no harm done (we still see its type separately).\n\t\tfn := g.info.TypeOf(decl.Name).(*types.Signature)\n\t\tfor params, i := fn.Params(), 0; i < params.Len(); i++ {\n\t\t\tg.see(params.At(i), obj)\n\t\t\tif params.At(i).Name() == \"\" {\n\t\t\t\tg.use(params.At(i), obj)\n\t\t\t}\n\t\t}\n\n\t\tif decl.Name.Name == \"_\" {\n\t\t\t// (9.9) objects named the blank identifier are used\n\t\t\tg.use(obj, nil)\n\t\t}\n\n\t\tif decl.Doc != nil {\n\t\t\tfor _, cmt := range decl.Doc.List {\n\t\t\t\tif strings.HasPrefix(cmt.Text, \"//go:cgo_export_\") {\n\t\t\t\t\t// (1.6) packages use functions exported to cgo\n\t\t\t\t\tg.use(obj, nil)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tdefault:\n\t\t// We do not cover BadDecl, but we shouldn't ever see one of those\n\t\tlint.ExhaustiveTypeSwitch(decl)\n\t}\n}\n\n// seeScope sees all objects in node's scope. If Options.LocalVariablesAreUsed is true, all objects that aren't fields\n// are marked as used. Variables set in skipLvars will not be marked as used.\nfunc (g *graph) seeScope(node ast.Node, by types.Object, skipLvars map[*types.Var]struct{}) {\n\t// A note on functions and scopes: for a function declaration, the body's BlockStmt can't be found in\n\t// types.Info.Scopes. Instead, the FuncType can, and that scope will contain receivers, parameters, return\n\t// parameters and immediate local variables.\n\n\tscope := g.info.Scopes[node]\n\tif scope == nil {\n\t\treturn\n\t}\n\tfor _, name := range scope.Names() {\n\t\tobj := scope.Lookup(name)\n\t\tg.see(obj, by)\n\n\t\tif g.opts.LocalVariablesAreUsed {\n\t\t\tif obj, ok := obj.(*types.Var); ok && !obj.IsField() {\n\t\t\t\tif _, ok := skipLvars[obj]; !ok {\n\t\t\t\t\tg.use(obj, by)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (g *graph) stmt(stmt ast.Stmt, by types.Object) {\n\tif stmt == nil {\n\t\treturn\n\t}\n\n\tfor {\n\t\t// We don't care about labels, so unwrap LabeledStmts. Note that a label can itself be labeled.\n\t\tif labeled, ok := stmt.(*ast.LabeledStmt); ok {\n\t\t\tstmt = labeled.Stmt\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tswitch stmt := stmt.(type) {\n\tcase *ast.AssignStmt:\n\t\tfor _, lhs := range stmt.Lhs {\n\t\t\tg.write(lhs, by)\n\t\t}\n\t\tfor _, rhs := range stmt.Rhs {\n\t\t\t// Note: it would be more accurate to have the rhs used by the lhs, but it ultimately doesn't matter,\n\t\t\t// because local variables always end up used, anyway.\n\t\t\t//\n\t\t\t// TODO(dh): we'll have to change that once we allow tracking the usedness of parameters\n\t\t\tg.read(rhs, by)\n\t\t}\n\n\tcase *ast.BlockStmt:\n\t\tg.block(stmt, by)\n\n\tcase *ast.BranchStmt:\n\t\t// Nothing to do\n\n\tcase *ast.DeclStmt:\n\t\tg.decl(stmt.Decl, by)\n\n\tcase *ast.DeferStmt:\n\t\tg.read(stmt.Call, by)\n\n\tcase *ast.ExprStmt:\n\t\tg.read(stmt.X, by)\n\n\tcase *ast.ForStmt:\n\t\tg.seeScope(stmt, by, nil)\n\t\tg.stmt(stmt.Init, by)\n\t\tg.read(stmt.Cond, by)\n\t\tg.stmt(stmt.Post, by)\n\t\tg.block(stmt.Body, by)\n\n\tcase *ast.GoStmt:\n\t\tg.read(stmt.Call, by)\n\n\tcase *ast.IfStmt:\n\t\tg.seeScope(stmt, by, nil)\n\t\tg.stmt(stmt.Init, by)\n\t\tg.read(stmt.Cond, by)\n\t\tg.block(stmt.Body, by)\n\t\tg.stmt(stmt.Else, by)\n\n\tcase *ast.IncDecStmt:\n\t\tif g.opts.PostStatementsAreReads {\n\t\t\tg.read(stmt.X, by)\n\t\t\tg.write(stmt.X, by)\n\t\t} else {\n\t\t\t// We treat post-increment as a write only. This ends up using fields, and sinks in tests, but not other\n\t\t\t// variables.\n\t\t\tg.write(stmt.X, by)\n\t\t}\n\n\tcase *ast.RangeStmt:\n\t\tg.seeScope(stmt, by, nil)\n\n\t\tg.write(stmt.Key, by)\n\t\tg.write(stmt.Value, by)\n\t\tg.read(stmt.X, by)\n\t\tg.block(stmt.Body, by)\n\n\tcase *ast.ReturnStmt:\n\t\tfor _, ret := range stmt.Results {\n\t\t\tg.read(ret, by)\n\t\t}\n\n\tcase *ast.SelectStmt:\n\t\tfor _, clause_ := range stmt.Body.List {\n\t\t\tclause := clause_.(*ast.CommClause)\n\t\t\tg.seeScope(clause, by, nil)\n\t\t\tswitch comm := clause.Comm.(type) {\n\t\t\tcase *ast.SendStmt:\n\t\t\t\tg.read(comm.Chan, by)\n\t\t\t\tg.read(comm.Value, by)\n\t\t\tcase *ast.ExprStmt:\n\t\t\t\tg.read(astutil.Unparen(comm.X).(*ast.UnaryExpr).X, by)\n\t\t\tcase *ast.AssignStmt:\n\t\t\t\tfor _, lhs := range comm.Lhs {\n\t\t\t\t\tg.write(lhs, by)\n\t\t\t\t}\n\t\t\t\tfor _, rhs := range comm.Rhs {\n\t\t\t\t\tg.read(rhs, by)\n\t\t\t\t}\n\t\t\tcase nil:\n\t\t\tdefault:\n\t\t\t\tlint.ExhaustiveTypeSwitch(comm)\n\t\t\t}\n\t\t\tfor _, body := range clause.Body {\n\t\t\t\tg.stmt(body, by)\n\t\t\t}\n\t\t}\n\n\tcase *ast.SendStmt:\n\t\tg.read(stmt.Chan, by)\n\t\tg.read(stmt.Value, by)\n\n\tcase *ast.SwitchStmt:\n\t\tg.seeScope(stmt, by, nil)\n\t\tg.stmt(stmt.Init, by)\n\t\tg.read(stmt.Tag, by)\n\t\tfor _, clause_ := range stmt.Body.List {\n\t\t\tclause := clause_.(*ast.CaseClause)\n\t\t\tg.seeScope(clause, by, nil)\n\t\t\tfor _, expr := range clause.List {\n\t\t\t\tg.read(expr, by)\n\t\t\t}\n\t\t\tfor _, body := range clause.Body {\n\t\t\t\tg.stmt(body, by)\n\t\t\t}\n\t\t}\n\n\tcase *ast.TypeSwitchStmt:\n\t\tg.seeScope(stmt, by, nil)\n\t\tg.stmt(stmt.Init, by)\n\t\tg.stmt(stmt.Assign, by)\n\t\tfor _, clause_ := range stmt.Body.List {\n\t\t\tclause := clause_.(*ast.CaseClause)\n\t\t\tg.seeScope(clause, by, nil)\n\t\t\tfor _, expr := range clause.List {\n\t\t\t\tg.read(expr, by)\n\t\t\t}\n\t\t\tfor _, body := range clause.Body {\n\t\t\t\tg.stmt(body, by)\n\t\t\t}\n\t\t}\n\n\tcase *ast.EmptyStmt:\n\t\t// Nothing to do\n\n\tdefault:\n\t\tlint.ExhaustiveTypeSwitch(stmt)\n\t}\n}\n\n// embeddedField sees the field declared by the embedded field node, and marks the type as used by the field.\n//\n// Embedded fields are special in two ways: they don't have names, so we don't have immediate access to an ast.Ident to\n// resolve to the field's types.Var and need to instead walk the AST, and we cannot use g.read on the type because\n// eventually we do get to an ast.Ident, and ObjectOf resolves embedded fields to the field they declare, not the type.\n// That's why we have code specially for handling embedded fields.\nfunc (g *graph) embeddedField(node ast.Node, by types.Object) *types.Var {\n\t// We need to traverse the tree to find the ast.Ident, but all the nodes we traverse should be used by the object we\n\t// get once we resolve the ident. Collect the nodes and process them once we've found the ident.\n\tnodes := make([]ast.Node, 0, 4)\n\tfor {\n\t\tswitch node_ := node.(type) {\n\t\tcase *ast.Ident:\n\t\t\t// obj is the field\n\t\t\tobj := g.info.ObjectOf(node_).(*types.Var)\n\t\t\t// the field is declared by the enclosing type\n\t\t\tg.see(obj, by)\n\t\t\tfor _, n := range nodes {\n\t\t\t\tg.read(n, obj)\n\t\t\t}\n\n\t\t\tif tname, ok := g.info.Uses[node_].(*types.TypeName); ok && tname.IsAlias() {\n\t\t\t\t// When embedding an alias we want to use the alias, not what the alias points to.\n\t\t\t\tg.use(tname, obj)\n\t\t\t} else {\n\t\t\t\tswitch typ := typeutil.Dereference(g.info.TypeOf(node_)).(type) {\n\t\t\t\tcase *types.Named:\n\t\t\t\t\t// (7.2) fields use their types\n\t\t\t\t\tg.use(typ.Obj(), obj)\n\t\t\t\tcase *types.Basic:\n\t\t\t\t\t// Nothing to do\n\t\t\t\tdefault:\n\t\t\t\t\t// Other types are only possible for aliases, which we've already handled\n\t\t\t\t\tlint.ExhaustiveTypeSwitch(typ)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn obj\n\t\tcase *ast.StarExpr:\n\t\t\tnode = node_.X\n\t\tcase *ast.SelectorExpr:\n\t\t\tnode = node_.Sel\n\t\t\tnodes = append(nodes, node_.X)\n\t\tcase *ast.IndexExpr:\n\t\t\tnode = node_.X\n\t\t\tnodes = append(nodes, node_.Index)\n\t\tcase *ast.IndexListExpr:\n\t\t\tnode = node_.X\n\t\tdefault:\n\t\t\tlint.ExhaustiveTypeSwitch(node_)\n\t\t}\n\t}\n}\n\n// isNoCopyType reports whether a type represents the NoCopy sentinel\n// type. The NoCopy type is a named struct with no fields and exactly\n// one method `func Lock()` that is empty.\n//\n// FIXME(dh): currently we're not checking that the function body is\n// empty.\nfunc isNoCopyType(typ types.Type) bool {\n\tst, ok := typ.Underlying().(*types.Struct)\n\tif !ok {\n\t\treturn false\n\t}\n\tif st.NumFields() != 0 {\n\t\treturn false\n\t}\n\n\tnamed, ok := types.Unalias(typ).(*types.Named)\n\tif !ok {\n\t\treturn false\n\t}\n\tswitch num := named.NumMethods(); num {\n\tcase 1, 2:\n\t\tfor i := range num {\n\t\t\tmeth := named.Method(i)\n\t\t\tif meth.Name() != \"Lock\" && meth.Name() != \"Unlock\" {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tsig := meth.Type().(*types.Signature)\n\t\t\tif sig.Params().Len() != 0 || sig.Results().Len() != 0 {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\tdefault:\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (g *graph) namedType(typ *types.TypeName, spec ast.Expr) {\n\t// (2.2) named types use the type they're based on\n\n\tif st, ok := spec.(*ast.StructType); ok {\n\t\tvar hasHostLayout bool\n\n\t\t// Named structs are special in that their unexported fields are only\n\t\t// used if they're being written to. That is, the fields are not used by\n\t\t// the named type itself, nor are the types of the fields.\n\t\tfor _, field := range st.Fields.List {\n\t\t\tseen := map[*types.Struct]struct{}{}\n\t\t\t// For `type x struct { *x; F int }`, don't visit the embedded x\n\t\t\tseen[g.info.TypeOf(st).(*types.Struct)] = struct{}{}\n\t\t\tvar hasExportedField func(t types.Type) bool\n\t\t\thasExportedField = func(T types.Type) bool {\n\t\t\t\tt, ok := typeutil.Dereference(T).Underlying().(*types.Struct)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif _, ok := seen[t]; ok {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tseen[t] = struct{}{}\n\t\t\t\tfor field := range t.Fields() {\n\t\t\t\t\tif field.Exported() {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\tif field.Embedded() && hasExportedField(field.Type()) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif len(field.Names) == 0 {\n\t\t\t\tfieldVar := g.embeddedField(field.Type, typ)\n\t\t\t\tif token.IsExported(fieldVar.Name()) && g.opts.ExportedIsUsed {\n\t\t\t\t\t// (6.2) structs use exported fields\n\t\t\t\t\tg.use(fieldVar, typ)\n\t\t\t\t}\n\t\t\t\tif g.opts.ExportedIsUsed && g.opts.ExportedFieldsAreUsed && hasExportedField(fieldVar.Type()) {\n\t\t\t\t\t// (6.5) structs use embedded structs that have exported fields (recursively)\n\t\t\t\t\tg.use(fieldVar, typ)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor _, name := range field.Names {\n\t\t\t\t\tobj := g.info.ObjectOf(name)\n\t\t\t\t\tg.see(obj, typ)\n\t\t\t\t\t// (7.2) fields use their types\n\t\t\t\t\t//\n\t\t\t\t\t// This handles aliases correctly because ObjectOf(alias) returns the TypeName of the alias, not\n\t\t\t\t\t// what the alias points to.\n\t\t\t\t\tg.read(field.Type, obj)\n\t\t\t\t\tif name.Name == \"_\" {\n\t\t\t\t\t\t// (9.9) objects named the blank identifier are used\n\t\t\t\t\t\tg.use(obj, typ)\n\t\t\t\t\t} else if token.IsExported(name.Name) && g.opts.ExportedIsUsed {\n\t\t\t\t\t\t// (6.2) structs use exported fields\n\t\t\t\t\t\tg.use(obj, typ)\n\t\t\t\t\t}\n\n\t\t\t\t\tif isNoCopyType(obj.Type()) {\n\t\t\t\t\t\t// (6.1) structs use fields of type NoCopy sentinel\n\t\t\t\t\t\tg.use(obj, typ)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// (6.6) if the struct has a field of type structs.HostLayout, then\n\t\t\t// this signals that all fields are relevant to match some\n\t\t\t// externally specified memory layout.\n\t\t\t//\n\t\t\t// This augments the 5.2 heuristic of using all fields when\n\t\t\t// converting via unsafe.Pointer. For example, 5.2 doesn't currently\n\t\t\t// handle conversions involving more than one level of pointer\n\t\t\t// indirection (although it probably should). Another example that\n\t\t\t// doesn't involve the use of unsafe at all is exporting symbols for\n\t\t\t// use by C libraries.\n\t\t\t//\n\t\t\t// The actual requirements for the use of structs.HostLayout fields\n\t\t\t// haven't been determined yet. It's an open question whether named\n\t\t\t// types of underlying type structs.HostLayout, aliases of it,\n\t\t\t// generic instantiations, or embedding structs that themselves\n\t\t\t// contain a HostLayout field count as valid uses of the marker (see\n\t\t\t// https://golang.org/issues/66408#issuecomment-2120644459)\n\t\t\t//\n\t\t\t// For now, we require a struct to have a field of type\n\t\t\t// structs.HostLayout or an alias of it, where the field itself may\n\t\t\t// be embedded. We don't handle fields whose types are type\n\t\t\t// parameters.\n\t\t\tfieldType := types.Unalias(g.info.TypeOf(field.Type))\n\t\t\tif fieldType, ok := fieldType.(*types.Named); ok {\n\t\t\t\tobj := fieldType.Obj()\n\t\t\t\tif obj.Name() == \"HostLayout\" && obj.Pkg().Path() == \"structs\" {\n\t\t\t\t\thasHostLayout = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// For 6.6.\n\t\tif hasHostLayout {\n\t\t\tg.useAllFieldsRecursively(typ.Type(), typ)\n\t\t}\n\t} else {\n\t\tg.read(spec, typ)\n\t}\n}\n\nfunc (g *SerializedGraph) color(rootID NodeID, states []nodeState) {\n\troot := g.nodes[rootID]\n\tif states[rootID].seen() {\n\t\treturn\n\t}\n\tstates[rootID] |= nodeStateSeen\n\tfor _, n := range root.uses {\n\t\tg.color(n, states)\n\t}\n}\n\ntype Object struct {\n\tName      string\n\tShortName string\n\t// OPT(dh): use an enum for the kind\n\tKind            string\n\tPath            ObjectPath\n\tPosition        token.Position\n\tDisplayPosition token.Position\n}\n\nfunc (g *SerializedGraph) Results() Result {\n\t// XXX objectpath does not return paths for unexported objects, which means that if we analyze the same code twice\n\t// (e.g. normal and test variant), then some objects will appear multiple times, but may not be used identically. we\n\t// have to deduplicate based on the token.Position. Actually we have to do that, anyway, because we may flag types\n\t// local to functions. Those are probably always both used or both unused, but we don't want to flag them twice,\n\t// either.\n\t//\n\t// Note, however, that we still need objectpaths to deduplicate exported identifiers when analyzing independent\n\t// packages in whole-program mode, because if package A uses an object from package B, B will have been imported\n\t// from export data, and we will not have column information.\n\t//\n\t// XXX ^ document that design requirement.\n\n\tstates := g.colorAndQuieten()\n\n\tvar res Result\n\t// OPT(dh): can we find meaningful initial capacities for the used and unused slices?\n\tfor _, n := range g.nodes[1:] {\n\t\tstate := states[n.id]\n\t\tif state.seen() {\n\t\t\tres.Used = append(res.Used, n.obj)\n\t\t} else if state.quiet() {\n\t\t\tres.Quiet = append(res.Quiet, n.obj)\n\t\t} else {\n\t\t\tres.Unused = append(res.Unused, n.obj)\n\t\t}\n\t}\n\n\treturn res\n}\n\nfunc (g *SerializedGraph) colorAndQuieten() []nodeState {\n\tstates := make([]nodeState, len(g.nodes)+1)\n\tg.color(0, states)\n\n\tvar quieten func(id NodeID)\n\tquieten = func(id NodeID) {\n\t\tstates[id] |= nodeStateQuiet\n\t\tfor _, owned := range g.nodes[id].owns {\n\t\t\tquieten(owned)\n\t\t}\n\t}\n\n\tfor _, n := range g.nodes {\n\t\tif states[n.id].seen() {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, owned := range n.owns {\n\t\t\tquieten(owned)\n\t\t}\n\t}\n\n\treturn states\n}\n\n// Dot formats a graph in Graphviz dot format.\nfunc (g *SerializedGraph) Dot() string {\n\tb := &strings.Builder{}\n\tstates := g.colorAndQuieten()\n\t// Note: We use addresses in our node names. This only works as long as Go's garbage collector doesn't move\n\t// memory around in the middle of our debug printing.\n\tdebugNode := func(n Node) {\n\t\tif n.id == 0 {\n\t\t\tfmt.Fprintf(b, \"n%d [label=\\\"Root\\\"];\\n\", n.id)\n\t\t} else {\n\t\t\tcolor := \"red\"\n\t\t\tif states[n.id].seen() {\n\t\t\t\tcolor = \"green\"\n\t\t\t} else if states[n.id].quiet() {\n\t\t\t\tcolor = \"grey\"\n\t\t\t}\n\t\t\tlabel := fmt.Sprintf(\"%s %s\\n%s\", n.obj.Kind, n.obj.Name, n.obj.Position)\n\t\t\tfmt.Fprintf(b, \"n%d [label=%q, color=%q];\\n\", n.id, label, color)\n\t\t}\n\t\tfor _, e := range n.uses {\n\t\t\tfmt.Fprintf(b, \"n%d -> n%d;\\n\", n.id, e)\n\t\t}\n\n\t\tfor _, owned := range n.owns {\n\t\t\tfmt.Fprintf(b, \"n%d -> n%d [style=dashed];\\n\", n.id, owned)\n\t\t}\n\t}\n\n\tfmt.Fprintf(b, \"digraph{\\n\")\n\tfor _, v := range g.nodes {\n\t\tdebugNode(v)\n\t}\n\n\tfmt.Fprintf(b, \"}\\n\")\n\n\treturn b.String()\n}\n\nfunc Graph(fset *token.FileSet,\n\tfiles []*ast.File,\n\tpkg *types.Package,\n\tinfo *types.Info,\n\tdirectives []lint.Directive,\n\tgenerated map[string]generated.Generator,\n\topts Options,\n) []Node {\n\tg := newGraph(fset, files, pkg, info, directives, generated, opts)\n\tg.entry()\n\treturn g.nodes\n}\n"
  },
  {
    "path": "unused/unused_test.go",
    "content": "package unused\n\nimport (\n\t\"fmt\"\n\t\"go/token\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"golang.org/x/tools/go/analysis/analysistest\"\n\t\"golang.org/x/tools/go/expect\"\n)\n\ntype expectation uint8\n\nconst (\n\tshouldBeUsed = iota\n\tshouldBeUnused\n\tshouldBeQuiet\n)\n\nfunc (exp expectation) String() string {\n\tswitch exp {\n\tcase shouldBeUsed:\n\t\treturn \"used\"\n\tcase shouldBeUnused:\n\t\treturn \"unused\"\n\tcase shouldBeQuiet:\n\t\treturn \"quiet\"\n\tdefault:\n\t\tpanic(\"unreachable\")\n\t}\n}\n\ntype key struct {\n\tident string\n\tfile  string\n\tline  int\n}\n\nfunc (k key) String() string {\n\treturn fmt.Sprintf(\"%s:%d\", k.file, k.line)\n}\n\nfunc relativePath(s string) string {\n\t// This is only used in a test, so we don't care about failures, or the cost of repeatedly calling os.Getwd\n\tcwd, err := os.Getwd()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ts, err = filepath.Rel(cwd, s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn s\n}\n\nfunc relativePosition(pos token.Position) string {\n\ts := pos.Filename\n\tif pos.IsValid() {\n\t\tif s != \"\" {\n\t\t\t// This is only used in a test, so we don't care about failures, or the cost of repeatedly calling os.Getwd\n\t\t\tcwd, err := os.Getwd()\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\ts, err = filepath.Rel(cwd, s)\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\ts += \":\"\n\t\t}\n\t\ts += fmt.Sprintf(\"%d\", pos.Line)\n\t\tif pos.Column != 0 {\n\t\t\ts += fmt.Sprintf(\":%d\", pos.Column)\n\t\t}\n\t}\n\tif s == \"\" {\n\t\ts = \"-\"\n\t}\n\treturn s\n}\n\nfunc check(t *testing.T, res *analysistest.Result) {\n\twant := map[key]expectation{}\n\tfiles := map[string]struct{}{}\n\n\tisTest := false\n\tfor _, f := range res.Pass.Files {\n\t\tfilename := res.Pass.Fset.Position(f.Pos()).Filename\n\t\tif strings.HasSuffix(filename, \"_test.go\") {\n\t\t\tisTest = true\n\t\t\tbreak\n\t\t}\n\t}\n\tfor _, f := range res.Pass.Files {\n\t\tfilename := res.Pass.Fset.Position(f.Pos()).Filename\n\t\tif !strings.HasSuffix(filename, \".go\") {\n\t\t\tcontinue\n\t\t}\n\t\tfiles[filename] = struct{}{}\n\t\tnotes, err := expect.ExtractGo(res.Pass.Fset, f)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tfor _, note := range notes {\n\t\t\tposn := res.Pass.Fset.PositionFor(note.Pos, false)\n\t\t\tswitch note.Name {\n\t\t\tcase \"quiet\":\n\t\t\t\tif len(note.Args) != 1 {\n\t\t\t\t\tt.Fatalf(\"malformed directive at %s\", posn)\n\t\t\t\t}\n\n\t\t\t\tif !isTest {\n\t\t\t\t\twant[key{note.Args[0].(string), posn.Filename, posn.Line}] = expectation(shouldBeQuiet)\n\t\t\t\t}\n\t\t\tcase \"quiet_test\":\n\t\t\t\tif len(note.Args) != 1 {\n\t\t\t\t\tt.Fatalf(\"malformed directive at %s\", posn)\n\t\t\t\t}\n\n\t\t\t\tif isTest {\n\t\t\t\t\twant[key{note.Args[0].(string), posn.Filename, posn.Line}] = expectation(shouldBeQuiet)\n\t\t\t\t}\n\t\t\tcase \"used\":\n\t\t\t\tif len(note.Args) != 2 {\n\t\t\t\t\tt.Fatalf(\"malformed directive at %s\", posn)\n\t\t\t\t}\n\n\t\t\t\tif !isTest {\n\t\t\t\t\tvar e expectation\n\t\t\t\t\tif note.Args[1].(bool) {\n\t\t\t\t\t\te = shouldBeUsed\n\t\t\t\t\t} else {\n\t\t\t\t\t\te = shouldBeUnused\n\t\t\t\t\t}\n\t\t\t\t\twant[key{note.Args[0].(string), posn.Filename, posn.Line}] = e\n\t\t\t\t}\n\t\t\tcase \"used_test\":\n\t\t\t\tif len(note.Args) != 2 {\n\t\t\t\t\tt.Fatalf(\"malformed directive at %s\", posn)\n\t\t\t\t}\n\n\t\t\t\tif isTest {\n\t\t\t\t\tvar e expectation\n\t\t\t\t\tif note.Args[1].(bool) {\n\t\t\t\t\t\te = shouldBeUsed\n\t\t\t\t\t} else {\n\t\t\t\t\t\te = shouldBeUnused\n\t\t\t\t\t}\n\t\t\t\t\twant[key{note.Args[0].(string), posn.Filename, posn.Line}] = expectation(e)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tcheckObjs := func(objs []Object, state expectation) {\n\t\tfor _, obj := range objs {\n\t\t\t// if t, ok := obj.Type().(*types.Named); ok && t.TypeArgs().Len() != 0 {\n\t\t\t// \tcontinue\n\t\t\t// }\n\t\t\tposn := obj.Position\n\t\t\tif _, ok := files[posn.Filename]; !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// This key isn't great. Because of generics, multiple objects (instantiations of a generic type) exist at\n\t\t\t// the same location. This only works because we ignore instantiations, but may lead to confusing test failures.\n\t\t\tk := key{obj.ShortName, posn.Filename, posn.Line}\n\t\t\texp, ok := want[k]\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"object at %s (%s) shouldn't exist but is %s (tests = %t)\", relativePosition(posn), obj.ShortName, state, isTest)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif false {\n\t\t\t\t// Sometimes useful during debugging, but too noisy to have enabled for all test failures\n\t\t\t\tt.Logf(\"%s handled by %q\", k, obj)\n\t\t\t}\n\t\t\tdelete(want, k)\n\t\t\tif state != exp {\n\t\t\t\tt.Errorf(\"object at %s (%s) should be %s but is %s (tests = %t)\", relativePosition(posn), obj.ShortName, exp, state, isTest)\n\t\t\t}\n\t\t}\n\t}\n\tures := res.Result.(Result)\n\tcheckObjs(ures.Used, shouldBeUsed)\n\tcheckObjs(ures.Unused, shouldBeUnused)\n\tcheckObjs(ures.Quiet, shouldBeQuiet)\n\n\tfor key, e := range want {\n\t\texp := e.String()\n\t\tt.Errorf(\"object at %s:%d should be %s but wasn't seen\", relativePath(key.file), key.line, exp)\n\t}\n}\n\nfunc TestAll(t *testing.T) {\n\tdirs, err := filepath.Glob(filepath.Join(analysistest.TestData(), \"src\", \"example.com\", \"*\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfor i, dir := range dirs {\n\t\tdirs[i] = filepath.Join(\"example.com\", filepath.Base(dir))\n\t}\n\n\tresults := analysistest.Run(t, analysistest.TestData(), Analyzer.Analyzer, dirs...)\n\tfor _, res := range results {\n\t\tcheck(t, res)\n\t}\n}\n"
  },
  {
    "path": "website/archetypes/default.md",
    "content": "---\ntitle: \"{{ replace .Name \"-\" \" \" | title }}\"\ndate: {{ .Date }}\ndraft: true\n---\n\n"
  },
  {
    "path": "website/assets/js/base.js",
    "content": "// Overwrites Docsy's main.js, which implements various things we don't use\n// - a top nav that fades away, looks cool with the promo block\n// - popovers for client-side search\n// - tooltips for the icons in the footer\n\n// Pretend that Cash is jQuery\nvar jQuery = $;\n"
  },
  {
    "path": "website/assets/scss/_styles_project.scss",
    "content": "// prevent footer from being drawn off-screen on short pages, and prevent the \"menu sliding out of view\" effect on long pages\n.td-sidebar__inner {\n\theight: auto;\n}\naside.td-sidebar-toc {\n\theight: unset;\n\tposition: unset;\n\toverflow: unset;\n}\naside.td-sidebar-toc > .td-toc {\n\theight: auto;\n\ttop: 6rem;\n\tposition: sticky;\n}\nbody > div.td-outer > div.td-main > div.row.flex-xl-nowrap {\n\theight: 100%;\n}\n\n.before-after {\n\tmargin-bottom: 0;\n}\n\n.before-after + .highlight {\n\tmargin-top: 0;\n}\n\n:target {\n\tbackground-color: #ffa;\n}\n\n@include media-breakpoint-up(md) {\n\thtml {\n\t\tscroll-padding-top: 5rem;\n\t}\n\n\t.td-offset-anchor {\n\t\tscroll-margin-top: -1rem\n\t}\n}\n\n\n// Undo Docsy's \"Adjust anchors vs the fixed menu.\"\n@include media-breakpoint-up(md) {\n\t.td-offset-anchor:target {\n\t\tdisplay: unset;\n\t\tposition: unset;\n\t\ttop: unset;\n\t\tvisibility: unset;\n\t}\n\n\th2[id]:before,\n\th3[id]:before,\n\th4[id]:before,\n\th5[id]:before {\n\t\tdisplay: unset;\n\t\tcontent: unset;\n\t\tmargin-top: unset;\n\t\theight: unset;\n\t\tvisibility: unset;\n\t}\n}\n\n// Undo Docsy limiting the width of headers\n.td-content {\n\t> h1, >h2 {\n\t\t@include media-breakpoint-up(lg) {\n\t\t\tmax-width: unset;\n\t\t}\n\t}\n}\n\n// Always show the navbar, no matter the screen size\n.td-sidebar-nav {\n\tdisplay: block !important;\n}\nbutton[data-toggle=\"collapse\"][data-target=\"#td-section-nav\"] {\n\tdisplay: none;\n}\n\n// Properly indent nested lists even on mobile layouts. Why is Docsy limiting this to the desktop version?\n.td-sidebar-nav__section .ul-1 ul {\n\tpadding-left: 1.5em;\n}\n\n#sponsors-bar {\n\tcolor: #fff;\n\tbackground-color: #F9F9F9;\n\n\tdiv.sponsor {\n\t\tmargin: 12px;\n\n\t\timg {\n\t\t\twidth: 160px;\n\t\t}\n\t}\n}\n\n.td-sidebar-nav .td-sidebar-link__page {\n\t// use the same weight and color for sections and pages. this is\n\t// fine because we never collapse sections.\n\tcolor: unset;\n\tfont-weight: $font-weight-medium;\n}\n\n.td-toc #TableOfContents a {\n\t// The ToC is already feint enough with it's font-weight, let's\n\t// not make it even harder to read\n\tcolor: unset;\n}\n\n@include media-breakpoint-up(md) {\n\t#getting-started-distribution-packages {\n\t\tdl {\n\t\t\tcolumns: 3;\n\t\t}\n\n\t\tdd {\n\t\t\tbreak-before: avoid;\n\t\t}\n\t}\n}\n\ndetails {\n\tmargin-bottom: 1rem;\n}\n"
  },
  {
    "path": "website/assets/scss/_variables_project.scss",
    "content": "$primary: #30638E;\n$secondary: #CE3262;\n"
  },
  {
    "path": "website/build.sh",
    "content": "#!/bin/sh\nset -e\nrm -rf ./public\nmkdir -p data\nmkdir -p content/docs/configuration/default_config\ngo run ./cmd/generate_checks/generate_checks.go >data/checks.json\ngo run ./cmd/generate_config/generate_config.go >content/docs/configuration/default_config/index.md\n\n(\n\tcd themes/docsy\n\t# --omit=dev so we don't try to install Hugo as an NPM module\n\tnpm install --omit=dev\n)\n\ngo run github.com/gohugoio/hugo@v0.110.0 --minify\n"
  },
  {
    "path": "website/cmd/generate_checks/generate_checks.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/quickfix\"\n\t\"honnef.co/go/tools/simple\"\n\t\"honnef.co/go/tools/staticcheck\"\n\t\"honnef.co/go/tools/stylecheck\"\n)\n\ntype Output struct {\n\tChecks     map[string]*lint.Documentation\n\tByCategory map[string][]string\n}\n\nfunc category(check string) string {\n\tidx := strings.IndexAny(check, \"0123456789\")\n\treturn check[:idx+1]\n}\n\nfunc main() {\n\toutput := Output{\n\t\tChecks:     map[string]*lint.Documentation{},\n\t\tByCategory: map[string][]string{},\n\t}\n\n\tgroups := [][]*lint.Analyzer{\n\t\tstaticcheck.Analyzers,\n\t\tsimple.Analyzers,\n\t\tstylecheck.Analyzers,\n\t\tquickfix.Analyzers,\n\t}\n\tfor _, group := range groups {\n\t\tfor _, a := range group {\n\t\t\tdoc := a.Doc.Compile()\n\t\t\tdoc.Text = convertText(doc.Text)\n\t\t\tdoc.TextMarkdown = convertText(doc.TextMarkdown)\n\t\t\toutput.Checks[a.Analyzer.Name] = doc\n\t\t\tg := output.ByCategory[category(a.Analyzer.Name)]\n\t\t\toutput.ByCategory[category(a.Analyzer.Name)] = append(g, a.Analyzer.Name)\n\t\t}\n\t}\n\n\tfor _, v := range output.ByCategory {\n\t\tsort.Strings(v)\n\t}\n\n\tout, err := json.MarshalIndent(output, \"\", \"\\t\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Println(string(out))\n}\n\nfunc moreCodeFollows(lines []string) bool {\n\tfor _, line := range lines {\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(line, \"    \") {\n\t\t\treturn true\n\t\t} else {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn false\n}\n\nvar alpha = regexp.MustCompile(`^[a-zA-Z ]+$`)\n\nfunc convertText(text string) string {\n\tvar buf bytes.Buffer\n\tlines := strings.Split(text, \"\\n\")\n\n\tinCode := false\n\tempties := 0\n\tfor i, line := range lines {\n\t\tif inCode {\n\t\t\tif !moreCodeFollows(lines[i:]) {\n\t\t\t\tif inCode {\n\t\t\t\t\tfmt.Fprintln(&buf, \"```\")\n\t\t\t\t\tinCode = false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprevEmpties := empties\n\t\tif line == \"\" && !inCode {\n\t\t\tempties++\n\t\t} else {\n\t\t\tempties = 0\n\t\t}\n\n\t\tif line == \"\" {\n\t\t\tfmt.Fprintln(&buf)\n\t\t\tcontinue\n\t\t}\n\n\t\tif strings.HasPrefix(line, \"    \") {\n\t\t\tline = line[4:]\n\t\t\tif !inCode {\n\t\t\t\tfmt.Fprintln(&buf, \"```go\")\n\t\t\t\tinCode = true\n\t\t\t}\n\t\t}\n\n\t\tonlyAlpha := alpha.MatchString(line)\n\t\tout := line\n\t\tif !inCode && prevEmpties >= 2 && onlyAlpha {\n\t\t\tfmt.Fprintf(&buf, \"#### %s\\n\", out)\n\t\t} else {\n\t\t\tfmt.Fprint(&buf, out)\n\t\t\tfmt.Fprintln(&buf)\n\t\t}\n\t}\n\tif inCode {\n\t\tfmt.Fprintln(&buf, \"```\")\n\t}\n\n\treturn strings.TrimSpace(buf.String())\n}\n"
  },
  {
    "path": "website/cmd/generate_config/generate_config.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"sort\"\n\n\t\"github.com/BurntSushi/toml\"\n\t\"honnef.co/go/tools/analysis/lint\"\n\t\"honnef.co/go/tools/config\"\n\t\"honnef.co/go/tools/quickfix\"\n\t\"honnef.co/go/tools/simple\"\n\t\"honnef.co/go/tools/staticcheck\"\n\t\"honnef.co/go/tools/stylecheck\"\n\t\"honnef.co/go/tools/unused\"\n)\n\nfunc main() {\n\tcfg := config.DefaultConfig\n\n\tchecks := []string{\"all\"}\n\tdo := func(analyzers ...*lint.Analyzer) {\n\t\tfor _, a := range analyzers {\n\t\t\tif a.Doc.NonDefault {\n\t\t\t\t// Use backticks to quote the check name so TOML doesn't escape them\n\t\t\t\tchecks = append(checks, fmt.Sprintf(\"-{{< check `%s` >}}\", a.Analyzer.Name))\n\t\t\t}\n\t\t}\n\t}\n\tdo(simple.Analyzers...)\n\tdo(staticcheck.Analyzers...)\n\tdo(stylecheck.Analyzers...)\n\tdo(unused.Analyzer)\n\tdo(quickfix.Analyzers...)\n\n\tsort.Slice(checks[1:], func(i, j int) bool {\n\t\treturn checks[i+1] < checks[j+1]\n\t})\n\n\tcfg.Checks = checks\n\n\tbuf := bytes.Buffer{}\n\ttoml.NewEncoder(&buf).Encode(cfg)\n\n\tr := regexp.MustCompile(`(?m)^[a-z_]+`)\n\tout := r.ReplaceAllString(buf.String(), \"{{< option `$0` >}}\")\n\n\tfmt.Println(\"---\")\n\tfmt.Println(\"headless: true\")\n\tfmt.Println(\"---\")\n\tfmt.Println(\"```toml\")\n\tfmt.Print(out)\n\tfmt.Println(\"```\")\n}\n"
  },
  {
    "path": "website/config.toml",
    "content": "baseURL = \"https://staticcheck.dev\"\nlanguageCode = 'en-us'\ntitle = 'Staticcheck'\ntheme = 'docsy'\nnavbar_logo = false\ndisableKinds = ['taxonomy', 'term']\ndisableAliases = true\nenableInlineShortcodes = true\n\n[outputs]\nhome = [\"HTML\", \"REDIR\"]\nsection = [\"HTML\"]\n\n[mediaTypes]\n[mediaTypes.\"text/netlify\"]\ndelimiter = \"\"\n\n[outputFormats]\n[outputFormats.REDIR]\nmediaType = \"text/netlify\"\nbaseName = \"_redirects\"\nisPlainText = true\nnotAlternative = true\n\n[markup.goldmark.renderer]\nunsafe = true\n\n[menu]\n[[menu.main]]\n  name = 'GitHub'\n  pre = \"<i class='fab fa-github'></i>\"\n  url = 'https://github.com/dominikh/go-tools'\n  weight = 999\n\n[markup]\n  [markup.highlight]\n      style = \"tango\"\n\n[params]\ndescription = 'Staticcheck is a state of the art linter for the Go programming language'\nimages = ['/img/logo.webp']\ntitle = 'Staticcheck'\n"
  },
  {
    "path": "website/content/_index.md",
    "content": "---\ntitle: Staticcheck\n---\n\n\n{{< blocks/lead type=\"section\" color=\"primary\" >}}\n<img style=\"height: auto; width: auto; max-height: 300px; max-width: 100%\" src=\"/img/logo.svg\" alt=\"Our mascot, the engineer\" />\n\nStaticcheck is a state of the art linter for the Go programming language.\nUsing static analysis, it finds bugs and performance issues, offers simplifications, and enforces style rules.\n\n\n<div class=\"mx-auto\">\n\t<a class=\"btn btn-lg btn-secondary mr-3 mb-4\" href=\"{{< relref \"/docs\" >}}\">\n\t\tGet started <i class=\"fas fa-arrow-alt-circle-right ml-2\"></i>\n\t</a>\n</div>\n{{< /blocks/lead >}}\n\n{{< blocks/section color=\"white\" >}}\n\n{{% blocks/feature title=\"Correctness\" icon=\"fa-check\" %}}\nCode obviously has to be correct. Unfortunately, it never\nis. Some code is more correct than other, but there'll\nalways be bugs. Tests catch some, your peers catch others,\nand Staticcheck catches some more.\n{{% /blocks/feature %}}\n\n\n{{% blocks/feature title=\"Simplicity\" icon=\"fa-circle\" %}}\nAfter correctness comes simplicity. There are many ways to\nskin a cat (but please don't), but some are unnecessarily\nelaborate. Staticcheck helps you replace complex code with\nsimple code.\n{{% /blocks/feature %}}\n\n{{% blocks/feature title=\"Maintainability\" icon=\"fa-screwdriver\" %}}\nCode is a living thing. If it's not maintained regularly,\nit will become stale and unreliable. It won't catch up\nwhen its dependencies move on or guidelines change.\nStaticcheck is like a sitter for your code for when you\ndon't have time.\n{{% /blocks/feature %}}\n\n\n{{% blocks/feature title=\"Exhaustiveness\" icon=\"fas fa-tasks\" url=\"/docs/checks\" %}}\nMore than\n{{< numchecks.inline >}}\n{{- $c := len $.Site.Data.checks.Checks -}}\n{{- sub $c (mod $c 25) -}}\n{{< /numchecks.inline >}}\nchecks ensure that your code is in tip-top shape.\n{{% /blocks/feature %}}\n\n\n{{% blocks/feature title=\"Integration\" icon=\"fab fa-github\"  %}}\nStaticcheck can easily be integrated with your code review and CI systems, preventing buggy code from ever getting committed.\n{{% /blocks/feature %}}\n\n\n{{% blocks/feature title=\"Editor integration\" icon=\"fa-i-cursor\" url=\"https://github.com/golang/tools/blob/master/gopls/doc/settings.md#staticcheck-bool\" %}}\nStaticcheck is part of most major Go editors and has been integrated with gopls, finding bugs and offering automatic fixes.\n{{% /blocks/feature %}}\n\n\n{{< /blocks/section >}}\n\n<link rel=\"prefetch\" href=\"/docs/\">\n"
  },
  {
    "path": "website/content/changes/2017.2.md",
    "content": "---\ntitle: Staticcheck 2017.2 release notes\nlinkTitle: 2017.2\nweight: -1\n---\n\nThe 2017.2 release of the staticcheck suite of tools focuses on\nreducing friction – fewer false positives, more tools for suppressing\nunwanted output, and JSON output for easier integration with other\ntools.\n\n## New features\n\n### Linter directives for ignoring problems\n\nIn the past, the only ways to ignore reported problems was by using\nthe `-ignore` flag. This led to overreaching ignore rules\nwhich weren't maintained regularly. Now, `//lint:ignore` and\n`//lint:file-ignore` comments can be used to ignore\nproblems, either on specific lines or file-wide. A full description of\nthese directives, their syntax and their behavior can be found\nin [the documentation]({{< relref \"/docs/configuration#ignoring-problems\" >}}).\n\nA related change adds the `-show-ignored` command line\nflag, which outputs problems that would otherwise be ignored by\ndirectives. This is primarily of use with the JSON output format,\nfor custom front ends.\n\n### Output formats\n\nAll staticcheck tools now support multiple output formats, selectable\nwith the `-f` flag.\n\nCurrently, two formats are supported. The first format is\n`text`, which is the default and uses the existing terminal\noutput format. The other is `json`, which emits JSON. The\noutput is a stream of objects, allowing for a future streaming output\nmode. Each object uses the following example schema:\n\n```json\n{\n  \"checker\": \"staticcheck\",\n  \"code\": \"SA4006\",\n  \"location\": {\n    \"file\": \"/usr/lib/go/src/database/sql/sql_test.go\",\n    \"line\": 2701,\n    \"column\": 5\n  },\n  \"message\": \"this value of err is never used\",\n  \"ignored\": false\n}\n```\n\n### Control over the exit code of megacheck\n\nMegacheck, the tool for running multiple checkers at once, now has\nper checker flags for controlling the overall exit code. Previously,\nmegacheck would exit non-zero if any checker found a problem. Now it\nis possible to configure for each checker whether it should cause a\nnon-zero exit, by using the `-&lt;checker&gt;.exit-non-zero`\nflags. This flag defaults to false for _gosimple_ and to true for\nthe other checkers.\n\n## Changes to checks\n\n### Support for `NoCopy` in _unused_\n\nThe _unused_ tool now understands `NoCopy` sentinel types. The\n`NoCopy` type, which is canonically a struct with no fields and only a\nsingle, empty `Lock` method, can be used to mark structs as not safe\nfor copying. By declaring a field of this type, _go vet_ will complain\nwhen it sees instances of the struct being copied.\n\nIn the past, _unused_ marked these fields as unused, now it ignores\nthem.\n\n### Detection of deprecated identifiers\n\n{{< check \"SA1019\" >}} now\ncorrectly identifies deprecated methods, in addition to fields and\npackage-level objects. Additionally, staticcheck now keeps track of\nwhen each identifier in the Go standard library was deprecated, so\nthat using `-go &lt;version&gt;` can correctly\nignore deprecation warnings that don't apply to the targeted Go\nversion.\n\n### Other\n\n- {{< check \"SA4017\" >}} no longer reports pure functions that are stubs – functions that immediately panic or return a constant.\n- {{< check \"SA5007\" >}} no longer flags infinite recursion when the function call is spawned as a new goroutine.\n- {{< check \"SA6002\" >}} now recognizes that `unsafe.Pointer` is a pointer type.\n- {{< check \"S1005\"  >}} no longer suggests `for range` when targeting a version older than Go 1.4.\n- {{< check \"S1026\"  >}} has been removed. In some rare instances, copying a string is necessary, and all common ways of doing this were incorrectly flagged by the check.\n\n\n## Other changes\n\n- The `-ignore` flag now supports ignoring checks in all packages, by using `*` as the path.\n- `//line` directives are now being ignored when reporting problems. That is, problems will always be reported for the actual position in the Go files they occur.\n- From now on, only the first compilation error encountered will be reported. \nThe tools expect to be run on valid Go code and there was little (if any) value in reporting all compilation errors encountered, especially because simple errors can lead to many follow-up errors.\n\n## Staticcheck 2017.2.1 Release Notes {#2017.2.1}\n\nThe 2017.2.1 release of the staticcheck suite of tools is the first\nbug fix release, fixing one bug.\n\n### Fixed bugs\n\nStaticcheck 2017.2 made the detection of deprecated objects\nGo-version aware. Unfortunately, this only worked correctly for\nfields and methods, but not package-level objects. This release\nfixes that.\n\n## Staticcheck 2017.2.2 Release Notes {#2017.2.2}\n\nThe 2017.2.2 release of the staticcheck suite of tools is the second\nbug fix release, fixing several bugs.\n\n### Fixed bugs\n\n- _unused_: correctly apply the NoCopy exemption when using the `-exported` flag.\n- _keyify_: support external test packages (`package foo_test`)\n- _staticcheck_: disable {{< check \"SA4005\" >}} – the check, in its current form, is prone to false positives and will be reimplemented in a future release.\n"
  },
  {
    "path": "website/content/changes/2019.1.md",
    "content": "---\ntitle: Staticcheck 2019.1 release notes\nlinkTitle: 2019.1\nweight: -2\n---\n\n## Big restructuring\n\nAt the core of the 2019.1 release lies the grand restructuring of all of the Staticcheck tools.\nAll of the individual checkers, as well as megacheck, have been merged into a single tool,\nwhich is simply called `staticcheck`.\nFrom this point forward, `staticcheck` will be _the_ static analyzer for Go code.\nIt will cover all of the existing categories of checks – bugs, simplifications, performance –\nas well as future categories, such as the new style checks.\n\nThis change makes a series of simplifications possible.\nPer-tool command line flags in megacheck have been replaced with unified flags\n(`-checks` and `-fail`)\nthat operate on arbitrary subsets of checks.\nConsumers of the JSON output no longer need to know about different checker names\nand can instead rely solely on user-controllable severities.\nAnd not to be neglected: gone is the silly name of _megacheck_.\n\nThis change will require some changes to your pipelines.\nEven though the gosimple, unused, and megacheck tools still exist, they have been deprecated\nand will be removed in the next release of staticcheck.\nAdditionally, megacheck's `-<tool>.exit-non-zero` flags have been rendered inoperable.\nInstead, you will have to use the `-fail` flag.\nFurthermore,, `-fail` defaults to `all`, meaning all checks will cause non-zero exiting.\nPrevious versions of megacheck had different defaults for different checkers, trying to guess the user's intention.\nInstead of guessing, staticcheck expects you to provide the correct flags.\n\nSince all of the tools have been merged into staticcheck, it will no longer run just one group of checks.\nThis may lead to additional problems being reported.\nTo restore the old behavior, you can use the new `-checks` flag.\n`-checks \"SA*\"` will run the same set of checks that the old staticcheck tool did.\nThe same flag should be used in place of megacheck's – now deprecated – `-<tool>.enabled` flags.\n\nDetails on all of the command-line flags can be found [in the documentation.]({{< relref \"/docs/#cli\" >}})\n\n## Configuration files\n\nStaticcheck 2019.1 adds support for per-project configuration files.\nWith these it will be possible to standardize and codify linter settings, the set of enabled checks, and more.\nPlease see the [documentation page on configuration]({{< relref \"/docs/#configuration\" >}}) for all the details!\n\n## Build system integration\n\nBeginning with this release, staticcheck calls out to the tools of the underlying build system\n(`go` for most people) to determine the list of Go files to process.\nThis change should not affect most people.\nIt does, however, have some implications:\nthe system that staticcheck runs on needs access to a full Go toolchain –\njust the source code of the standard library no longer suffices.\nFurthermore, setting `GOROOT` to use a different Go installation no longer works as expected.\nInstead, `PATH` has to be modified so that `go` resolves to the desired Go command.\n\nThis change has been necessary to support Go modules.\nAdditionally, it will allow us to support alternative build systems such as Bazel in the future.\n\n## Handling of broken packages\n\nWe have redesigned the way staticcheck handles broken packages.\nPreviously, if you ran `staticcheck ...` and any package wouldn't compile,\nstaticcheck would refuse to check any packages whatsoever.\nNow, it will skip broken packages, as well as any of their dependents, and check only the remaining packages.\nAny build errors that are encountered will be reported as problems.\n\n## Checks\n\n### New checks\n\nStaticcheck 2019.1 adds a new category of checks, ST1.\nST1 contains checks for common style violations – poor variable naming, incorrectly formatted comments and the like.\nIt brings the good parts of [golint](https://github.com/golang/lint) to staticcheck,\nand adds some checks of its own.\n\nIn addition, some other checks have been added.\n\n{{< check \"S1032\" >}} recommends replacing `sort.Sort(sort.StringSlice(...))` with `sort.Strings(...)`;\nsimilarly for other types that have helpers for sorting.\n\n{{< check \"SA9004\" >}} flags groups of constants where only the first one is given an explicit type.\n\n{{< check \"SA1025\" >}} checks for incorrect uses of `(*time.Timer).Reset`.\n\n### Changed checks\n\nSeveral checks have been tweaked, either making them more accurate or finding more issues.\n\n{{< check \"S1002\" >}} no longer applies to code in tests.\nWhile `if aBool == true` is usually an anti-pattern,\nit can feel more natural in unit tests,\nas it mirrors the `if got != want` pattern.\n\n{{< check \"S1005\" >}} now flags `for x, _ := range` because of the unnecessary blank assignment.\n\n{{< check \"S1007\" >}} no longer suggests using raw strings for regular expressions containing backquotes.\n\n{{< check \"S1016\" >}} now considers the targeted Go version.\nIt will no longer suggest type conversions between struct types with different field tags\nunless Go 1.8 or later is being targeted.\n\n{{< check \"SA1000\" >}} now checks arguments passed to the `regexp.Match` class of functions.\n\n{{< check \"SA1014\" >}} now checks arguments passed to `(*encoding/xml.Decoder).DecodeElement`.\n\n{{< check \"SA6002\" >}} now realizes that unsafe.Pointer is a pointer.\n\n{{< check \"U1000\" >}} has fewer false positives in the presence of embedding.\n\n### Removed checks\n\n{{< check \"S1013\" >}} has been removed,\nno longer suggesting replacing `if err != nil { return err }; return nil` with `return err`.\nThis check has been the source of contention and more often than not, it reduced the consistency of the surrounding code.\n\n## Deprecation notices\n\nThis release deprecates various features of staticcheck.\nThese features will be removed in the next release.\n\nAs already explained earlier, the _unused_, _gosimple_, and _megacheck_ tools\nhave been replaced by _staticcheck_.\nSimilarly, the flags `-<tool>.enabled` and `-<tool>.exit-non-zero`\nhave been replaced by `-checks` and `-fail`.\nFinally, the `-ignore` flag has been replaced\nby [linter directives]({{< relref \"/docs/#ignoring-problems\" >}}).\n\n## Binary releases\n\nBeginning with this release, we're publishing\n[prebuilt binaries to GitHub](https://github.com/dominikh/go-tools/releases).\nThese releases still require a functioning Go installation in order to operate, however.\n\n## Other changes\n\nWe've removed the `-min_confidence` flag.\nThis flag hasn't been doing anything for years.\n\nA new formatter called [Stylish]({{< relref \"/docs/running-staticcheck/cli/formatters\" >}}) (usable with `-f stylish`)\nprovides output that is designed for easier consumption by humans.\n\nDue to the restructuring of checkers, the `checker` field in JSON output has been replaced\nwith the `severity` field.\n\n## Staticcheck 2019.1.1 Release Notes {#2019.1.1}\n\nThe 2019.1.1 release of Staticcheck is the first bug fix release, fixing several bugs and improving performance.\n\n### Changes\n\n- The ST category of checks no longer flag style issues of\n  aliased types when the aliased type exists in a package\n  we aren't explicitly checking. This avoids crashes and\n  erratic error reports.\n- Compiler errors now have correct position information.\n- A crash in the Stylish reporter has been fixed.\n- We no longer flag unused objects that belong to cgo internals.\n- The {{< check \"U1000\" >}} check has been optimized, reducing its memory\n  usage and runtime.\n"
  },
  {
    "path": "website/content/changes/2019.2.md",
    "content": "---\ntitle: Staticcheck 2019.2 release notes\nlinkTitle: 2019.2\nweight: -3\n---\n\n\n## Performance improvements {#performance}\n\nStaticcheck 2019.2 brings major performance improvements and a\nreduction in memory usage.\n\nStaticcheck has been redesigned to only keep those packages in memory that are actively being processed.\nThis allows for much larger workspaces to be checked in one go.\nWhile previously it may have been necessary to split a list of packages into many invocations of `staticcheck`,\nthis is now handled intelligently and efficiently by Staticcheck itself.\n\nIn particular, memory usage is now closely tied to parallelism:\nhaving more CPU cores available allows for more packages to be processed in parallel,\nwhich increases the number of packages held in memory at any one time.\nNot only does this make good use of available resources –\nsystems with more CPU cores also tend to have more memory available –\nit also exposes a single, easy to use knob for trading execution time for memory use.\nBy setting `GOMAXPROCS` to a value lower than the number of available cores,\nmemory usage of Staticcheck will be reduced, at the cost of taking longer to complete.\n\nWe've observed reductions in memory usage of 2x to 8x when checking large code bases.\n\n<table class=\"table\">\n  <thead>\n    <tr>\n      <th>Package</th>\n      <th>2019.1.1</th>\n      <th>2019.2¹</th>\n      <th>Change</th>\n    </tr>\n  </thead>\n\n  <tr>\n    <td>net/http</td>\n    <td>3.543 s / 677 MB</td>\n    <td>3.747 s / 254 MB</td>\n    <td>+5.76% / -62.48%</td>\n  </tr>\n\n  <tr>\n    <td>strconv</td>\n    <td>1.628 s / 294 MB</td>\n    <td>1.678 s / 118 MB</td>\n    <td>+3.07% / -59.86%</td>\n  </tr>\n\n  <tr>\n    <td>image/color</td>\n    <td>1.304 s / 225 MB</td>\n    <td>1.702 s / 138 MB</td>\n    <td>+30.52% / -38.67%</td>\n  </tr>\n\n  <tr>\n    <td>std</td>\n    <td>26.234 s / 3987 MB</td>\n    <td>19.444 s / 1054 MB</td>\n    <td>-25.88% / -73.56%</td>\n  </tr>\n\n  <tr>\n    <td>github.com/cockroachdb/cockroach/pkg/...</td>\n    <td>88.644 s / 15959 MB</td>\n    <td>93.798 s / 4156 MB</td>\n    <td>+5.81% / -73.96%</td>\n  </tr>\n\n  <tfoot>\n    <tr>\n      <td colspan=\"4\">\n        ¹: The fact cache was empty for all benchmarks.\n      </td>\n    </tr>\n  </tfoot>\n</table>\n\nIn addition, Staticcheck now employs caching to speed up repeated checking of packages.\nIn the past, when checking a package, all of its dependencies had to be loaded from source and analyzed.\nNow, we can make use of Go's build cache, as well as cache our own analysis facts.\nThis makes Staticcheck behave a lot more like `go build`, where repeated builds are much faster.\n\n| Package                                  | Uncached           | Cached             | Change                 |\n|------------------------------------------|--------------------|--------------------|------------------------|\n| net/http                                 | 3.747 s / 254 MB   | 1.545 s / 195 MB   | -58.77% / -23.23%      |\n| strconv                                  | 1.678 s / 118 MB   | 0.495 s / 57 MB    | -70.5% / -51.69%       |\n| image/color                              | 1.702 s / 138 MB   | 0.329 s / 31 MB    | -80.67% / -77.54%      |\n| std                                      | 19.444 s / 1054 MB | 15.099 s / 887 MB  | -22.35% / -15.84%      |\n| github.com/cockroachdb/cockroach/pkg/... | 93.798 s / 4156 MB | 47.205 s / 2516 MB | -49.67% / -39.46%      |\n\nThis combination of improvements not only compensates for the\nincreased memory usage that 2019.1 introduced, it also brings the\nmemory usage and execution times way below the levels of those seen in the\n2017.2 release, which had previously been our most efficient\nrelease.\n\nIt should be noted that all of these improvements are part of the `staticcheck` command itself, not the individual checks.\nTools such as golangci-lint will have to replicate our efforts to benefit from these improvements.\n\n## The go/analysis framework {#go-analysis}\n\nPart of the redesign of Staticcheck involved porting our code to the [go/analysis](https://godoc.org/golang.org/x/tools/go/analysis) framework.\n\nThe go/analysis framework is a framework for writing static analysis tools such as Staticcheck and go vet.\nIt provides an API that enables interoperability between different analyses and analysis drivers – drivers being the code that actually executes analyses.\nThe intention is that any driver can trivially use any analysis that is implemented using go/analysis.\n\nWith the exception of {{< check \"U1000\" >}}, all of our checks are now go/analysis analyses. Furthermore, the `staticcheck` command is now a go/analysis driver.\n\nWith our move to this framework, we enable other drivers to reuse our checks without having to patch them.\nThis should be of particular interest to golangci-lint, which previously took to patching Staticcheck, sometimes in subtly incorrect ways.\nAnother high-profile go/analysis driver is gopls, the Go language server. It will now be much easier for gopls to use Staticcheck to analyze code, should it so desire.\n\nTheoretically it would also allow us to use third-party analyses as part of Staticcheck.\nDue to quality control reasons, however, we will likely refrain from doing so.\nNonetheless it would be trivial for users to maintain internal forks of `cmd/staticcheck` that use third-party analyses.\n\n## Improvements to the CLI {#cli}\n\nWe've made several minor improvements to the command-line interface of `staticcheck` that improve usability and debuggability.\n\n### SIGINFO handler {#cli-siginfo}\n\nUpon receiving the SIGINFO signal – or SIGUSR1 on platforms that lack\nSIGINFO – Staticcheck will dump statistics, such as the current phase\nand how many packages are left to analyze.\n\n```text\nPackages: 37/619 initial, 38/1011 total; Workers: 8/8; Problems: 73\n```\n\n### Explaining checks {#cli-explain}\n\nUsing the new `-explain` flag, a check's documentation can be displayed right in the terminal,\neliminating the need to visit the website.\n\n```text\n$ staticcheck -explain S1007\nSimplify regular expression by using raw string literal\n\nRaw string literals use ` instead of \" and do not support\nany escape sequences. This means that the backslash (\\) can be used\nfreely, without the need of escaping.\n\nSince regular expressions have their own escape sequences, raw strings\ncan improve their readability.\n\nBefore:\n\n    regexp.Compile(\"\\\\A(\\\\w+) profile: total \\\\d+\\\\n\\\\z\")\n\nAfter:\n\n    regexp.Compile(`\\A(\\w+) profile: total \\d+\\n\\z`)\n\nAvailable since\n    2017.1\n```\n\n### -debug.version {#cli-debug-version}\n\nThe `-debug.version` flag causes `staticcheck` to print\ndetailed version information, such as the Go version used to compile\nit, as well as the versions of all dependencies if built using Go\nmodules. This feature is intended for debugging issues, and we will\nask for its output from users who file issues.\n\n```text\n$ staticcheck -debug.version\nstaticcheck (devel, v0.0.0-20190602125119-5a4a2f4a438d)\n\nCompiled with Go version: go1.12.5\nMain module:\n\thonnef.co/go/tools@v0.0.0-20190602125119-5a4a2f4a438d (sum: h1:U5vSGN1Bjr0Yd/4pRcp8iRUCs3S5TIPzoAeTEFV2aiU=)\nDependencies:\n\tgithub.com/BurntSushi/toml@v0.3.1 (sum: h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=)\n\tgolang.org/x/tools@v0.0.0-20190530171427-2b03ca6e44eb (sum: h1:mnQlcVx8Qq8L70HV0DxUGuiuAtiEHTwF1gYJE/EL9nU=)\n```\n\n### Enabling unused's whole program mode {#cli-unused}\n\nWhen we merged `unused` into `staticcheck`, we lost the ability to specify the `-exported` flag to report unused exported identifiers.\nStaticcheck 2019.2 restores this ability with the new `-unused.whole-program` flag.\n\n### Range information in diagnostics {#cli-ranges}\n\nMany of our checks now emit `[start, end]` ranges for findings instead of just positions.\nThese ranges can be accessed via the `json` output formatter, as well as by using `go/analysis.Diagnostic` directly, such as in gopls.\n\nNote that not all checks are able to emit range information.\n\n## Installing Staticcheck as a module {#module}\n\nAs part of the 2019.2 release, we've turned Staticcheck into a Go module.\nFrom now on, if using Go modules, you can install specific versions of Staticcheck with `go get honnef.co/go/tools/cmd/staticcheck@<version>`,\nthough do note that older releases do not have a `go.mod` file.\nYou can still download them as modules, but Go will record indirect dependencies in the main module's `go.mod` file, and no minimum versions are specified.\n\nStaticcheck will not use Semantic Versioning for its releases.\nIt is our belief that Semver is a poor fit for applications and is more suited towards libraries.\nFor example, almost every release of Staticcheck has backwards incompatible changes to some APIs that aren't meant for public consumption,\nbut which we expose nevertheless so that tinkerers can use them.\n\nHowever, we use so-called _pre-release versions_ of the form `v0.0.0-2019.2`.\nThese allow us to embed our versioning scheme in that of Semver, with correct sorting and updating of versions.\nFurthermore, these versions ensure that `go get ...`,\nif not specifying an explicit version (that is, if using the query `latest`),\nwill install the latest released version of Staticcheck and not the master branch.\n\nWhile you can use these pre-release version numbers directly, you can also use the canonical versions of the form `2019.2` instead.\nThe Go tool will automatically translate these versions to the appropriate pre-releases.\n\nTo install the master branch, use `go get honnef.co/go/tools/cmd/staticcheck@master`\n\n## Removal of deprecated functionality {#deprecated}\n\nStaticcheck 2019.1 deprecated the `unused`, `gosimple`, and `megacheck`\nutilities, as they have been merged into `staticcheck`. Furthermore, it deprecated the `-ignore` flag,\nwhich has been replaced by [linter directives]({{< relref \"/docs/#ignoring-problems\" >}}).\n\nThis release no longer includes these deprecated utilities, nor does\nit provide the deprecated flag.\n\n## Checks {#checks}\n\n### New checks {#checks-new}\n\nNumerous new checks have been added in this release:\n\n- {{< check \"S1033\"  >}} flags unnecessary guards around calls to `delete`.\n- {{< check \"S1034\"  >}} simplifies type switches involving redundant type assertions.\n- {{< check \"SA1026\" >}} flags attempts at marshaling invalid types.\n- {{< check \"SA1027\" >}} flags incorrectly aligned atomic accesses.\n- {{< check \"SA4020\" >}} flags unreachable case clauses in type switches.\n- {{< check \"SA4021\" >}} flags calls to append with a single argument, as `x = append(y)` is equivalent to `x = y`.\n- {{< check \"SA5008\" >}} flags certain kinds of invalid struct tags.\n- {{< check \"SA5009\" >}} verifies the correctness of Printf calls.\n- {{< check \"SA6005\" >}} flags inefficient string comparisons involving `strings.ToLower`\n  or `strings.ToUpper` when they can be replaced with `strings.EqualFold`.\n- {{< check \"SA9005\" >}} flags attempts at marshaling structs with no public fields nor custom marshaling.\n- {{< check \"ST1017\" >}} flags so-called [yoda conditions](https://en.wikipedia.org/wiki/Yoda_conditions),\n  which take the form of `if 42 == x`.\n- {{< check \"ST1018\" >}} flags string literals containing zero-width characters.\n\n\n### Changed checks {#checks-changed}\n\nSeveral checks have been improved:\n\n- {{< check \"SA1019\" >}} now flags imports of deprecated packages.\n- {{< check \"SA4000\" >}} no longer flags comparisons between custom float types. Additionally, it avoids a false positive caused by cgo.\n- {{< check \"SA4006\" >}} no longer flags unused values in code generated by goyacc. This avoids noise caused by the nature of the generated state machine.\n- {{< check \"ST1005\" >}} no longer flags error messages that start with capitalized type names.\n- {{< check \"ST1006\" >}} no longer flags receiver names in generated code.\n- {{< check \"SA5002\" >}} no longer suggests replacing `for false {` with `for {`.\n- Added \"SIP\" and \"RTP\" as default initialisms to {{< check \"ST1003\" >}}.\n- {{< check \"SA1006\" >}}, {{< check \"SA4003\" >}}, {{< check \"S1017\" >}}, and {{< check \"S1020\" >}} match more code patterns.\n- {{< check \"S1021\"  >}} is less eager to merge declarations and assignments when multiple assignments are involved.\n- {{< check \"U1000\"  >}} has been rewritten, eliminating a variety of false positives.\n\n## Sustainable open source and a personal plea {#sustainable-open-source}\n\nStaticcheck is an open source project developed primarily by me, Dominik Honnef, in my free time.\nWhile this model of software development has gotten increasingly common, it is not very sustainable.\nTime has to be split between open source work and paid work to sustain one's life.\nThis is made especially unfortunate by the fact that hundreds of companies rely on open source each day,\nbut few consider giving back to it, even though it would directly benefit their businesses,\nensuring that the software they rely on keeps being developed.\n\nI have long been soliciting donations for Staticcheck [on Patreon](https://www.patreon.com/dominikh) to make its development more sustainable.\nA fair number of individuals have generously pledged their support and I am very grateful to them.\nUnfortunately, only few companies support Staticcheck's development, and I'd like for that to change.\n\nTo people who are familiar with Patreon, it might've always seemed like an odd choice for a software project.\nPatreon focuses on art and creative work, and on individuals supporting said work, not companies.\nI am therefore excited to announce my participation in [GitHub Sponsors](https://github.com/sponsors),\na new way of supporting developers, directly on GitHub.\n\nGitHub Sponsors allows you to easily support developers by sponsoring them on a monthly basis, [via a few simple clicks.](https://github.com/users/dominikh/sponsorship)\nIt is fully integrated with the platform and can use your existing billing information, making it an effortless process.\n**To encourage more company sponsorships I offer to display your company's logo prominently on\n[Staticcheck's website](https://staticcheck.dev/)**\nfor\n[$250 USD a month](https://github.com/users/dominikh/sponsorship?utf8=%E2%9C%93&tier_id=MDIyOk1hcmtldHBsYWNlTGlzdGluZ1BsYW4yNTAy&editing=false),\nto show my appreciation for your contribution and to show to the world how much you care about code quality.\n\nPlease don't hesitate [contacting me directly](mailto:dominik@honnef.co) if neither GitHub Sponsors nor Patreon seem suitable to you but you'd like to support me nevertheless.\nI am sure we can work something out.\n\n## Staticcheck 2019.2.1 release notes {#2019.2.1}\n\nThe 2019.2 release has an unfortunate bug that prevents Staticcheck from running on 32-bit architectures, causing it to crash unconditionally.\nThis release fixes that crash.\n\n## Staticcheck 2019.2.2 release notes {#2019.2.2}\n\nStaticcheck 2019.2.2 contains the following user-visible fixes:\n\n- {{< check \"S1008\" >}} now skips if/else statements where both branches return the same value.\n- {{< check \"SA4006\" >}} now considers a value read when a switch statement reads it, even if the switch statement has no branches.\n- 2019.2 introduced a bug that made it impossible to enable non-default checks via configuration files. This is now possible again.\n- 2019.2 introduced a bug that made the `-tags` command line argument ineffective, making it impossible to pass in build tags. This is now possible again.\n- From this release onward, we will use pseudo versions of the form\n  `v0.0.1-<year>.<minor>` instead of `v0.0.0-<year>.<minor>`.\n  This fixes an issue where `go get` would prefer an older commit over a newer released version due to the way versions sort.\n\n## Staticcheck 2019.2.3 release notes {#2019.2.3}\n\nStaticcheck 2019.2.3 is a re-release of 2019.2.2.\nIts pre-built binaries, which can be found on GitHub,\nhave been built with Go 1.13, to enable checking of code that uses language features introduced in Go 1.13.\n"
  },
  {
    "path": "website/content/changes/2020.1.md",
    "content": "---\ntitle: Staticcheck 2020.1 release notes\nlinkTitle: 2020.1\nweight: -4\n---\n\n## Introduction to Staticcheck 2020.1 {#introduction}\n\nStaticcheck 2020.1 introduces UI improvements, speed enhancements, and\na number of [new](#checks-new) as well as [improved](#checks-changed) checks. Additionally, it is the\nfirst release to support the upcoming Go 1.14.\n\n## UI improvements {#ui-improvements}\n\nWe've improved the output of the `staticcheck` command as well as\nStaticcheck's integration with [gopls](https://github.com/golang/tools/tree/master/gopls) to make it easier to understand\nthe problems that are being reported.\n\nRelated information describes the source of a problem, or why\nStaticcheck believes that there is a problem. Take the following\npiece of code for example:\n\n```go\nfunc fn(x *int) {\n\tif x == nil {\n\t\tlog.Println(\"x is nil, returning\")\n\t}\n\t// lots of code here\n\tlog.Println(*x)\n}\n```\n\nStaticcheck 2020.1 will produce the following output:\n\n```text\nfoo.go:6:14: possible nil pointer dereference (SA5011)\n\tfoo.go:2:5: this check suggests that the pointer can be nil\n```\n\nThe actual problem that is being reported is the \"possible nil pointer\ndereference\". Staticcheck also explains why it believes that `x` might\nbe nil, namely the comparison on line 2.\n\nWhen using the [`text`]({{< relref \"/docs/running-staticcheck/cli/formatters#text\" >}}) or [`stylish`]({{< relref \"/docs/running-staticcheck/cli/formatters#stylish\" >}}) formatters, related information will\nappear as indented lines. The [`json`]({{< relref \"/docs/running-staticcheck/cli/formatters#json\" >}}) formatter adds a new field\n`related` to problems, containing position information as well as the\nmessage. Editors that use gopls will also display the related\ninformation.\n\nRelated information should make it easier to understand why Staticcheck\nis flagging code, and how to fix problems.\n\nIntegration with gopls has seen some other improvements as well¹. We\nnow emit better position information that more accurately reflects the\ntrue source of a problem. The most obvious example is that a missing\npackage comment will no longer underline the entire file. Similarly,\ninvalid function arguments will be highlighted individually, instead\nof highlighting the call as a whole. Finally, some problems can now be\nautomatically fixed by using quick fixes.\n\n¹: due to the nature of Staticcheck's integration with gopls, gopls\nwill need to update their dependency on Staticcheck before benefiting\nfrom these changes.\n\n## Better caching {#caching}\n\nThe 2019.2 release introduced caching to Staticcheck, greatly speeding\nup repeated runs. However, the caching only applied to dependencies;\nthe packages under analysis still had to be analyzed anew on every\ninvocation to compute the list of problems. Staticcheck 2020.1\nintroduces caching of problems found, so that repeat runs for\nunchanged packages are virtually instantaneous.\n\n## Checks {#checks}\n### New checks {#checks-new}\n\nNumerous new checks have been added in this release:\n\n- {{< check \"S1035\"  >}} flags redundant calls to `net/http.CanonicalHeaderKey`.\n- {{< check \"S1036\"  >}} flags unnecessary guards around map accesses.\n- {{< check \"S1037\"  >}} flags unnecessarily elaborate ways of sleeping.\n- {{< check \"S1038\"  >}} flags unnecessary uses of `fmt.Sprintf`, such as `fmt.Println(fmt.Sprintf(...))`.\n- {{< check \"S1039\"  >}} flags uses of `fmt.Sprint` with single string literals.\n- {{< check \"SA1028\" >}} flags uses of `sort.Slice` on non-slices.\n- {{< check \"SA1029\" >}} flags inappropriate keys in calls to context.WithValue.\n- {{< check \"SA4022\" >}} flags comparisons of the kind `if &x == nil`.\n- {{< check \"SA5010\" >}} flags impossible type assertions.\n- {{< check \"SA5011\" >}} flags potential nil pointer dereferences.\n- {{< check \"ST1019\" >}} flags duplicate imports.\n- {{< check \"ST1020\" >}} checks the documentation of exported functions.\n- {{< check \"ST1021\" >}} checks the documentation of exported types.\n- {{< check \"ST1022\" >}} checks the documentation of exported variables and constants.\n\n{{< check \"ST1020\" >}}, {{< check \"ST1021\" >}} and {{< check \"ST1022\" >}} are not enabled by default.\n\n### Changed checks {#checks-changed}\n\nSeveral checks have been improved:\n\n- {{< check \"S1036\"  >}} detects more kinds of unnecessary guards around map accesses.\n- {{< check \"S1008\"  >}} reports more easily understood diagnostics.\n- {{< check \"S1025\"  >}} no longer suggests using `v.String()` instead of `fmt.Sprintf(\"%s\", v)` when `v` is a `reflect.Value`. `fmt` gives special treatment to `reflect.Value` and the two results differ.\n- {{< check \"SA1015\" >}} no longer flags uses of `time.Tick` in packages that implement [Cobra](https://github.com/spf13/cobra) commands.\n- {{< check \"SA1019\" >}} no longer misses references to deprecated packages when said packages have been vendored.\n- {{< check \"SA4000\" >}} no longer flags comparisons of the kind `x == x` and `x != x` when `x` has a compound type involving floats.\n- {{< check \"SA4003\" >}} no longer flags comparisons of the kind `x <= 0` when `x` is an unsigned integer. While it is true that `x <= 0` can be written more specifically as `x == 0`, this is not a helpful suggestion in reality. A lot of people use `x <= 0` as a defensive measure, in case `x` ever becomes signed. Also, unlike all the other warnings made in the check, `x <= 0` is neither a tautology nor a contradiction, it is merely less precise than it could be.\n- {{< check \"SA4016\" >}} now detects silly bitwise ops of the form `x & k` where `k` is defined as `const k = iota`.\n- {{< check \"SA4018\" >}} no longer flags self-assignments involving side effects; for example, it won't flag `x[fn()] = x[fn()]` if `fn` isn't pure.\n- {{< check \"SA5008\" >}} now permits duplicate instances of various struct tags used by `github.com/jessevdk/go-flags`.\n- {{< check \"SA5009\" >}} now correctly recognizes that `unsafe.Pointer` is a pointer type that can be used with verbs such as `%p`. Furthermore, it validates calls to `golang.org/x/xerrors.Errorf`.\n- {{< check \"SA5009\" >}} now understands `fmt.Printf` verbs that were changed and added in Go 1.13. Specifically, it now recognizes the new `%O` verb, and allows the use of `%x` and `%X` on floats and complex numbers.\n- {{< check \"ST1003\" >}} has learned about several new initialisms.\n- {{< check \"ST1011\" >}} no longer misses variable declarations with inferred types.\n- {{< check \"ST1016\" >}} now ignores the names of method receivers of methods declared in generated files.\n- {{< check \"ST1020\" >}}, {{< check \"ST1021\" >}}, and {{< check \"ST1022\" >}} no longer enforce comment style in generated code.\n\n## General bug fixes {#bugs}\n\nThe following bugs were fixed:\n\n- A race condition in the {{< check \"U1000\" >}} check could occasionally lead to sporadic false positives.\n- Some files generated by _goyacc_ weren't recognized as being generated.\n- `staticcheck` no longer fails to check packages that consist exclusively of tests.\n\n\n## Staticcheck 2020.1.1 release notes {#2020.1.1}\n\nThe 2020.1 release neglected to update the version string stored in\nthe binary, causing `staticcheck -version` to incorrectly emit `(no version)`.\n\n## Staticcheck 2020.1.2 release notes {#2020.1.2}\n\nThe 2020.1.1 release incorrectly identified itself as version 2020.1.\n\n## Staticcheck 2020.1.3 release notes {#2020.1.3}\n\nThis release fixes two bugs involving `//lint:ignore` directives:\n\n- When ignoring U1000 and checking a package that contains tests,\n  Staticcheck would incorrectly complain that the linter directive\n  didn't match any problems, even when it did.\n- On repeated runs, the position information for a <q>this linter directive didn't match anything</q> report\n  would either be missing, or be wildly incorrect.\n\n## Staticcheck 2020.1.4 release notes {#2020.1.4}\n\nThis release adds special handling for imports of the\ndeprecated `github.com/golang/protobuf/proto` package.\n\n[github.com/golang/protobuf](https://github.com/golang/protobuf)\nhas deprecated the `proto` package, but\ntheir `protoc-gen-go` still imports the package and uses\none of its constants, <q>to enforce a weak dependency on a\nsufficiently new version of the legacy package</q>.\n\nStaticcheck would flag the import of this deprecated package in all\ncode generated by protoc-gen-go. Instead of forcing the project to\nchange their project structure, we choose to ignore such imports in\ncode generated by protoc-gen-go. The import still gets flagged in code\nnot generated by protoc-gen-go.\n\nYou can find more information about this in the [upstream issue](https://github.com/golang/protobuf/issues/1077).\n\n## Staticcheck 2020.1.5 release notes {#2020.1.5}\n\nThis release fixes a [crash in the pattern matching engine]({{< issueref 806 >}})\nand a [false positive in SA4006]({{< issueref 733 >}}).\n\n## Staticcheck 2020.1.6 release notes {#2020.1.6}\n\nThis release makes the following fixes and improvements:\n\n- Staticcheck no longer panics when encountering files that have the following comment: `// Code generated DO NOT EDIT.`\n- {{< check \"SA4016\" >}} no longer panics when checking bitwise operations that involve dot-imported identifiers.\n- Fixed the suggested fix offered by {{< check \"S1004\" >}}.\n- Fixed a false positive involving byte arrays in {{< check \"SA5009\" >}}.\n- Fixed a false positive involving named byte slice types in {{< check \"SA5009\" >}}.\n- Added another heuristic to avoid flagging function names in error messages in {{< check \"ST1005\" >}}.\n- {{< check \"SA3000\" >}} will no longer flag missing calls to `os.Exit` in `TestMain` functions if targeting Go 1.15 or newer.\n\n\n"
  },
  {
    "path": "website/content/changes/2020.2.md",
    "content": "---\ntitle: Staticcheck 2020.2 release notes\nlinkTitle: \"2020.2 (v0.1.0)\"\nweight: -5\n---\n\n## Performance improvements {#performance-improvements}\n\nThe primary focus of this release is a major improvement in performance, significantly reducing memory usage while also reducing runtimes.\n\n<table class=\"table\">\n    <caption>Uncached, GOMAXPROCS=1</caption>\n    <thead>\n      <tr>\n        <th>Package</th>\n        <th>2020.1.6</th>\n        <th>2020.2</th>\n        <th>Delta</th>\n        <th>Stats</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>image/color </td>\n        <td>2.41s ±19% </td>\n        <td>2.00s ±14% </td>\n        <td>-17.08%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n     <tr>\n        <td>k8s.io/kubernetes/pkg/... </td>\n        <td>276s ± 1% </td>\n        <td>219s ± 1% </td>\n        <td>-20.62%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>net/http </td>\n        <td>6.18s ± 1% </td>\n        <td>5.61s ± 5% </td>\n        <td>-9.21%</td>\n        <td>p=0.000, n=8+10</td>\n      </tr>\n      <tr>\n        <td>std </td>\n        <td>49.5s ± 1% </td>\n        <td>42.5s ± 1% </td>\n        <td>-14.04%</td>\n        <td>p=0.000, n=9+10</td>\n      </tr>\n      <tr>\n        <td>strconv </td>\n        <td>2.49s ± 9% </td>\n        <td>2.19s ±12% </td>\n        <td>-12.08%</td>\n        <td>p=0.001, n=10+10</td>\n      </tr>\n    </tbody>\n    <tbody>\n      <tr>\n        <td>image/color </td>\n        <td>167MB ±26% </td>\n        <td>146MB ±19% </td>\n        <td>-12.62%</td>\n        <td>p=0.043, n=10+10</td>\n      </tr>\n      <tr>\n        <td>k8s.io/kubernetes/pkg/... </td>\n        <td>2.14GB ± 1% </td>\n        <td>0.45GB ±13% </td>\n        <td>-79.09%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>net/http </td>\n        <td>216MB ± 6% </td>\n        <td>166MB ±18% </td>\n        <td>-23.11%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>std </td>\n        <td>972MB ± 3% </td>\n        <td>284MB ± 9% </td>\n        <td>-70.82%</td>\n        <td>p=0.000, n=8+10</td>\n      </tr>\n      <tr>\n        <td>strconv </td>\n        <td>155MB ±21% </td>\n        <td>139MB ±29% </td>\n        <td>~</td>\n        <td>p=0.063, n=10+10</td>\n      </tr>\n    </tbody>\n</table>\n\n<table class=\"table\">\n    <caption>Cached, GOMAXPROCS=1</caption>\n    <thead>\n      <tr>\n        <th>Package</th>\n        <th>2020.1.6</th>\n        <th>2020.2</th>\n        <th>Delta</th>\n        <th>Stats</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>image/color </td>\n        <td>160ms ± 0% </td>\n        <td>107ms ± 7% </td>\n        <td>-33.13%</td>\n        <td>p=0.000, n=8+10</td>\n      </tr>\n      <tr>\n        <td>k8s.io/kubernetes/pkg/... </td>\n        <td>12.7s ± 1% </td>\n        <td>6.9s ± 1% </td>\n        <td>-45.26%</td>\n        <td>p=0.000, n=9+10</td>\n      </tr>\n      <tr>\n        <td>net/http </td>\n        <td>370ms ± 0% </td>\n        <td>230ms ± 0% </td>\n        <td>-37.84%</td>\n        <td>p=0.000, n=8+8</td>\n      </tr>\n      <tr>\n        <td>std </td>\n        <td>2.52s ± 1% </td>\n        <td>1.31s ± 1% </td>\n        <td>-48.13%</td>\n        <td>p=0.000, n=10+9</td>\n      </tr>\n      <tr>\n        <td>strconv </td>\n        <td>164ms ± 4% </td>\n        <td>110ms ± 0% </td>\n        <td>-32.93%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n    </tbody>\n    <tbody>\n      <tr>\n        <td>image/color </td>\n        <td>38.6MB ± 4% </td>\n        <td>20.8MB ± 1% </td>\n        <td>-45.96%</td>\n        <td>p=0.000, n=9+10</td>\n      </tr>\n      <tr>\n        <td>k8s.io/kubernetes/pkg/... </td>\n        <td>863MB ± 4% </td>\n        <td>283MB ± 2% </td>\n        <td>-67.28%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>net/http </td>\n        <td>70.5MB ± 5% </td>\n        <td>25.8MB ± 2% </td>\n        <td>-63.48%</td>\n        <td>p=0.000, n=10+9</td>\n      </tr>\n      <tr>\n        <td>std </td>\n        <td>243MB ±16% </td>\n        <td>73MB ± 8% </td>\n        <td>-70.00%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>strconv </td>\n        <td>37.2MB ± 2% </td>\n        <td>21.3MB ± 1% </td>\n        <td>-42.76%</td>\n        <td>p=0.000, n=9+10</td>\n      </tr>\n    </tbody>\n</table>\n\n<table class=\"table\">\n    <caption>Uncached, GOMAXPROCS=32</caption>\n    <thead>\n      <tr>\n        <th>Package</th>\n        <th>2020.1.6</th>\n        <th>2020.2</th>\n        <th>Delta</th>\n        <th>Stats</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>image/color </td>\n        <td>1.19s ±21% </td>\n        <td>1.06s ±12% </td>\n        <td>~</td>\n        <td>p=0.115, n=10+8</td>\n      </tr>\n      <tr>\n        <td>k8s.io/kubernetes/pkg/... </td>\n        <td>27.0s ± 2% </td>\n        <td>22.4s ± 2% </td>\n        <td>-16.96%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>net/http </td>\n        <td>2.24s ±11% </td>\n        <td>2.23s ±10% </td>\n        <td>~</td>\n        <td>p=0.870, n=10+10</td>\n      </tr>\n      <tr>\n        <td>std </td>\n        <td>7.14s ± 5% </td>\n        <td>5.10s ± 9% </td>\n        <td>-28.56%</td>\n        <td>p=0.000, n=10+9</td>\n      </tr>\n      <tr>\n        <td>strconv </td>\n        <td>1.24s ±26% </td>\n        <td>1.18s ±21% </td>\n        <td>~</td>\n        <td>p=0.753, n=10+10</td>\n      </tr>\n    </tbody>\n    <tbody>\n      <tr>\n        <td>image/color </td>\n        <td>143MB ± 7% </td>\n        <td>141MB ± 6% </td>\n        <td>~</td>\n        <td>p=0.515, n=8+10</td>\n      </tr>\n      <tr>\n        <td>k8s.io/kubernetes/pkg/... </td>\n        <td>5.77GB ± 6% </td>\n        <td>2.76GB ± 4% </td>\n        <td>-52.25%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>net/http </td>\n        <td>284MB ±10% </td>\n        <td>226MB ±14% </td>\n        <td>-20.38%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>std </td>\n        <td>1.74GB ±10% </td>\n        <td>1.15GB ±14% </td>\n        <td>-34.11%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>strconv </td>\n        <td>148MB ±18% </td>\n        <td>144MB ±16% </td>\n        <td>~</td>\n        <td>p=0.579, n=10+10</td>\n      </tr>\n    </tbody>\n</table>\n\n\n<table class=\"table\">\n    <caption>Cached, GOMAXPROCS=32</caption>\n    <thead>\n      <tr>\n        <th>Package</th>\n        <th>2020.1.6</th>\n        <th>2020.2</th>\n        <th>Delta</th>\n        <th>Stats</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>image/color </td>\n        <td>96.0ms ± 6% </td>\n        <td>80.0ms ± 0% </td>\n        <td>-16.67%</td>\n        <td>p=0.000, n=10+9</td>\n      </tr>\n      <tr>\n        <td>k8s.io/kubernetes/pkg/... </td>\n        <td>4.64s ± 1% </td>\n        <td>3.88s ± 0% </td>\n        <td>-16.22%</td>\n        <td>p=0.000, n=9+8</td>\n      </tr>\n      <tr>\n        <td>net/http </td>\n        <td>216ms ± 3% </td>\n        <td>167ms ± 4% </td>\n        <td>-22.69%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>std </td>\n        <td>1.09s ± 2% </td>\n        <td>0.96s ± 2% </td>\n        <td>-12.20%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>strconv </td>\n        <td>100ms ± 0% </td>\n        <td>87ms ± 8% </td>\n        <td>-13.00%</td>\n        <td>p=0.000, n=9+10</td>\n      </tr>\n    </tbody>\n    <tbody>\n      <tr>\n        <td>image/color </td>\n        <td>46.4MB ± 3% </td>\n        <td>24.1MB ± 5% </td>\n        <td>-48.08%</td>\n        <td>p=0.000, n=8+10</td>\n      </tr>\n      <tr>\n        <td>k8s.io/kubernetes/pkg/... </td>\n        <td>1.38GB ± 9% </td>\n        <td>0.27GB ± 1% </td>\n        <td>-80.29%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>net/http </td>\n        <td>80.7MB ±12% </td>\n        <td>31.4MB ± 2% </td>\n        <td>-61.16%</td>\n        <td>p=0.000, n=10+8</td>\n      </tr>\n      <tr>\n        <td>std </td>\n        <td>363MB ±12% </td>\n        <td>75MB ± 7% </td>\n        <td>-79.30%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n      <tr>\n        <td>strconv </td>\n        <td>48.5MB ± 6% </td>\n        <td>24.4MB ± 3% </td>\n        <td>-49.72%</td>\n        <td>p=0.000, n=10+10</td>\n      </tr>\n    </tbody>\n</table>\n\n\nSee commit [5cfc85b70e7b778eb76fd7338e538d7c9af21e4e](https://github.com/dominikh/go-tools/commit/5cfc85b70e7b778eb76fd7338e538d7c9af21e4e)\nfor details on how these improvements have been achieved.\n\nFurthermore, Staticcheck 2020.2 will skip very large packages (currently packages that are 50 MiB or larger),\nunder the assumption that these packages contain bundled assets and aren't worth analyzing.\nThis might further reduce Staticcheck's memory usage in your projects.\n\n## Changes to the detection of unused code {#unused}\n\n### Removal of whole-program mode and changes to the handling of exported identifiers {#unused-whole-program}\n\nThe [aforementioned performance improvements](#performance-improvements) necessitate some changes to the _U1000_ check (also known as _unused_).\n\nThe most visible change is the removal of the _whole program_ mode.\nThis mode, which analyzed an entire program and reported unused code even if it is exported,\ndid not work well with the kind of caching that we use in Staticcheck.\nEven in previous versions, it didn't always work correctly and may have caused flaky results,\ndepending on the state of the cache and the order of `staticcheck` invocations.\n\nThe whole-program mode may be revived in the future as a standalone tool,\nwith the understanding that this mode of operation is inherently more expensive than `staticcheck`.\nIn the meantime, if you depend on this functionality and can tolerate its bugs, you should continue using Staticcheck 2020.1.\n\nAs part of improving the correctness of U1000, changes were made to the normal mode as well.\nIn particular, **all** exported package-level identifiers will be considered used from now on,\neven if these identifiers are declared in `package main` or tests, even if they are otherwise unused.\nExported identifiers in `package main` can be used in ways invisible to us, for example via the `plugin` build mode.\nFor tests, we would run into the same kind of issues as we did with the whole program mode.\n\n### Improvements {#unused-improvements}\n\nThe `//lint:ignore` directive now works more intelligently with the U1000 check.\nIn previous versions, the directive would only suppress the output of a diagnostic. For example, for the following example\n\n```go\npackage pkg\n\n//lint:ignore U1000 This is fine.\nfunc fn1() { fn2() }\n\nfunc fn2() {}\n```\n\nStaticcheck would emit the following output:\n\n```text\nfoo.go:6:6: func fn2 is unused (U1000)\n```\n\nas it would only suppress the diagnostic for `fn1`.\n\nBeginning with this release, the directive instead actively marks the identifier as used,\nwhich means that any transitively used code will also be considered used, and no diagnostic will be reported for `fn2`.\nSimilarly, the `//lint:file-ignore` directive will consider everything in a file used, which may transitively mark code in other files used, too.\n\n## UI improvements {#ui-improvements}\n\nWe've made some minor improvements to the output and behavior of the `staticcheck` command:\n\n- the command now prints instructions on how to look up documentation for checks\n- output of the `-explain` flag includes a link to the online documentation\n- a warning is emitted when a package pattern matches no packages\n- unmatched ignore directives cause `staticcheck` to exit with a non-zero status code\n\n## Changes to versioning scheme {#versioning-improvements}\n\nStaticcheck releases have two version numbers: one meant for human consumption and one meant for consumption by machines, via Go modules.\nFor example, the previous release was both `2020.1.6` (for humans) and `v0.0.1-2020.1.6` (for machines).\n\nIn previous releases, we've tried to include the human version in the machine version, by using the `v0.0.1-<human version>` scheme.\nHowever, this scheme had various drawbacks.\nFor this and future releases we've switched to a more standard scheme for machine versions: `v0.<minor>.<patch>`.\nMinor will increase by one for every feature release of Staticcheck,\nand patch will increase by one for every bugfix release of Staticcheck,\nresetting to zero on feature releases.\n\nFor example, this release is both `2020.2` and `v0.1.0`.\nA hypothetical `2020.2.1` would be `v0.1.1`, and `2021.1` will be `v0.2.0`.\nThis new versioning scheme fixes various issues when trying to use Staticcheck as a Go module.\nIt will also allow us to make true pre-releases in the future.\n\nDocumentation on the website, as well as the output of `staticcheck -version`, will include both version numbers, to make it easier to associate the two.\n\nFor detailed information on how we arrived at this decision, see the discussion on [issue 777]({{< issueref 777 >}}).\n\n## Checks {#checks}\n### New checks {#checks-new}\n\nThe following new checks have been added:\n\n- {{< check \"SA4023\" >}} flags impossible comparisons of interface values with untyped nils\n- {{< check \"SA5012\" >}} flags function calls with slice arguments that aren't the right length\n- {{< check \"SA9006\" >}} flags dubious bit shifts of fixed size integers\n\n### Changed checks {#checks-changed}\n\nSeveral checks have been improved:\n\n- {{< check \"S1030\"  >}} no longer recommends replacing `m[string(buf.Bytes())]` with `m[buf.String()]`, as the former gets optimized by the compiler\n- {{< check \"S1008\"  >}} no longer incorrectly suggests that the negation of `>=` is `<=`\n- {{< check \"S1029\"  >}} and {{ check \"SA6003\" }} now also check custom types with underlying type `string`\n- {{< check \"SA1019\" >}} now recognizes deprecation notices that aren't in the last paragraph of a comment\n- {{< check \"SA1019\" >}} now emits more precise diagnostics for deprecated code in the standard library\n- {{< check \"SA4006\" >}} no longer flags assignments where the value is a typed nil\n- {{< check \"SA5011\" >}} is now able to detect more functions that never return, thus reducing the number of false positives\n- {{< check \"SA9004\" >}} no longer assumes that constants belong to the same group when they have different types\n- Automatic fixes for {{< check \"SA9004\" >}} inside gopls no longer incorrectly duplicate comments\n- {{< check \"ST1003\" >}} no longer complains about ALL_CAPS in variable names that don't contain any letters\n- Incorrect position information in various checks have been fixed\n- Crashes in various checks have been fixed\n\n## Staticcheck 2020.2.1 release notes {#2020.2.1}\n\nThis release eliminates some false negatives as well as false positives, makes the `staticcheck` command less noisy and fixes a potential security issue.\n\n- {{< check \"SA4020\" >}} no longer claims that `case nil` is an unreachable case in a type switch.\n- {{< check \"S1025\"  >}} no longer marks uses of `Printf` as unnecessary when the printed types implement the `fmt.Formatter` interface.\n- Various checks may now detect bugs in conditional code that were previously missed. This was a regression introduced in Staticcheck 2020.1.\n- The `staticcheck` command no longer reminds the user of the `-explain` flag every time problems are found. This was deemed too noisy.\n- We've updated our dependency on `golang.org/x/tools` to guard against arbitrary code execution on Windows.\n  Note that to be fully safe, you will also have to update your installation of Go.\n  See the [Command PATH security in Go](https://blog.golang.org/path-security) article by the Go authors for more information on this potential vulnerability.\n\n## Staticcheck 2020.2.2 release notes {#2020.2.2}\n\nThis release fixes a rare crash in Staticcheck, reduces the number of false positives, and adds support for Go 1.16's `io/fs.FileMode` type.\n\n- {{< check \"SA9002\" >}} now supports the new `io/fs.FileMode` type in addition to `os.FileMode`.\n- Various checks no longer crash when analyzing nested function calls involving multiple return values.\n- {{< check \"SA1006\" >}} no longer flags `Printf` calls of the form `Printf(fn())` when `fn` has multiple return values.\n- Staticcheck now understands the effects of `github.com/golang/glog` on a function's control flow.\n\n## Staticcheck 2020.2.3 release notes {#2020.2.3}\n\nThis release fixes a false positive in U1000. See [issue 942]({{< issueref 942 >}}) for an example.\n\n## Staticcheck 2020.2.4 release notes {#2020.2.4}\n\nThis release fixes the following issues:\n\n- A false positive in {{< check \"S1017\" >}} when the `len` function has been shadowed\n- A bug in Staticcheck's intermediate representation\n  that would lead to nonsensical reports claiming that a value isn't being used\n  when it is definitely being used\n  (see issues [949]({{< issueref 949 >}}) and [981]({{< issueref 981 >}}) for more information)\n- A rare crash (see issue [972]({{< issueref 972 >}}) for more information)\n"
  },
  {
    "path": "website/content/changes/2021.1.md",
    "content": "---\ntitle: Staticcheck 2021.1 release notes\nlinkTitle: \"2021.1 (v0.2.0)\"\nweight: -6\n---\n\n## UI improvements {#ui-improvements}\n\nThe new `-list-checks` flag lists all available checks, showing each check's identifier and one-line description.\nYou can use the existing `-explain` flag to find out more about each individual check.\n\n### Targeted Go version {#targeted-go-version}\n\nSome checks in Staticcheck adjust their behavior based on the targeted Go version. For example, the suggestion to use `for range` instead of `for _ = range` does not apply to Go 1.3 and earlier.\n\nIn the past, the default Go version that was targeted was the version that Staticcheck had been compiled with. For most users, this meant that it targeted the latest Go release.\nGoing forward, we will default to the Go version declared in `go.mod` via the `go` directive.\nEven though this value does not exactly correspond to the module's minimum supported Go version, it is still a better apprximation than \"whatever Go version Staticcheck has been compiled with\",\nand should work fine for most users.\n\nAs before, the targeted Go version can be explicitly set by using the `-go` flag.\n\n## Checks {#checks}\n\n### New checks {#checks-new}\n\nThe following new checks have been added:\n\n- {{< check \"S1040\"  >}} flags type assertions from an interface type to itself\n- {{< check \"SA1030\" >}} flags invalid arguments to various functions in the `strconv` package\n- {{< check \"SA4005\" >}} flags assignments to fields on value receivers that intended the receiver to be a pointer instead\n- {{< check \"SA4024\" >}} flags pointless comparisons of the values of `len` and `cap` with zero\n- {{< check \"SA4025\" >}} flags suspicious integer division that results in zero, such as `2 / 3`\n- {{< check \"SA4026\" >}} flags constant expressions that try to express [negative zero](https://en.wikipedia.org/wiki/Signed_zero)\n- {{< check \"SA4027\" >}} flags no-op attempts at modifying a `(*net/url.URL)`'s query string\n- {{< check \"ST1023\" >}} flags variable declarations of the form `var x T = v` where the type `T` is redundant; this check is disabled by default\n\n\n### Changed checks {#checks-changed}\n\nThe following checks have been improved:\n\n- {{< check \"S1025\"  >}} now recommends converting byte slices to strings instead of using `fmt.Sprintf`\n- {{< check \"S1008\"  >}} includes fewer unnecessary parentheses and double negations in its suggested fixes\n- {{< check \"S1017\"  >}} is now able to flag calls that use string literals and integer literals\n- {{< check \"SA9005\" >}} now includes the value's type in its output\n- {{< check \"ST1000\" >}}, {{< check \"ST1020\" >}}, {{< check \"ST1021\" >}}, and {{< check \"ST1022\" >}} no longer flag effectively empty comments, including those that consist entirely of directives\n\n## Restructured documentation {#documentation}\n\nThe documentation on [the website](https://staticcheck.dev) has been restructured and hopefully made more approachable.\nInstead of being one long document, it is now split into multiple smaller articles.\nIn the future, more articles that look at specific aspects of Staticcheck will be added.\n\n## Better integration with gopls {#gopls}\n\nSeveral behind the scenes changes prepare this release for better integration with [gopls](https://github.com/golang/tools/blob/master/gopls/README.md).\nThis will include more accurate severities for diagnostics as well as numerous new refactorings.\nThese improvements will be part of a future gopls release.\n\n## Deletion of rdeps {#rdeps}\n\nThe rdeps tool has been deleted.\nThis was a GOPATH-centric tool that allowed finding all reverse dependencies of a Go package.\nBoth the move to Go modules as well as the emergence of much better tooling for inspecting dependencies (such as [goda](https://github.com/loov/goda)) has made rdeps redundant.\n\n## Staticcheck 2021.1.1 release notes {#2021.1.1}\n\nThis release adds support for new language features in Go 1.17,\nnamely conversions from slices to array pointers,\nthe unsafe.Add function,\nand the unsafe.Slice function.\n\nAdditionally, it fixes the following false positives:\n\n- {{< check \"ST1000\" >}} no longer flags package docs that start with whitespace if they're otherwise well-formed.\n- {{< check \"SA5002\" >}} no longer prints one too many percent signs in its message.\n- {{< check \"SA4000\" >}} no longer flags comparisons between floats.\n- {{< check \"SA4010\" >}} no longer flags appends to slices that might share their backing array with other slices.\n- {{< check \"SA5011\" >}} no longer infers possible nil pointer dereferences from comparisons done outside of control flow constructs.\n  This avoids false positives when using `assert`-style functions.\n  See [issue 1022]({{< issueref 1022 >}}) for a concrete example.\n- {{< check \"S1020\"  >}} no longer flags nested if statements when the inner statement has an else branch.\n- {{< check \"SA5011\" >}} no longer claims that indexing a nil slice will cause a nil pointer dereference.\n\n## Staticcheck 2021.1.2 release notes {#2021.1.2}\n\nThis release fixes the following false positives:\n\n- {{< check \"SA4000\" >}} no longer flags operations involving the `math/rand` package. This is to allow patterns such as `rand.Intn(2) - rand.Intn(2)`.\n- {{< check \"S1019\" >}} no longer recommends replacing `make(map[X]Y, 0)` with `make(map[X]Y)` – the latter may allocate a small starting size.\n- {{< check \"SA1026\" >}} now more accurately implements the behavior of `encoding/json` and `encoding/xml`, which will lead to fewer false positives involving method sets or embedding.\n  It might also find bugs that it previously missed.\n- Checks that are sensitive to control flow, such as {{< check \"SA5011\" >}}, now recognize functions from `k8s.io/klog` and `go.uber.org/zap` that affect control flow (`Panic` and `Fatal` related functions.)\n\nFurthermore, it fixes the following crashes:\n\n- Using `//lint:ignore U1000` on a type alias would crash.\n- Converting a slice to an array pointer, where the array type is a named type, would crash.\n"
  },
  {
    "path": "website/content/changes/2022.1.md",
    "content": "---\ntitle: Staticcheck 2022.1 release notes\nlinkTitle: \"2022.1 (v0.3.0)\"\nweight: -7\n---\n\n## Improvements {#improvements}\n\nThis release adds support for Go 1.18 and type parameters (generics).\n\nFurthermore, it adds two new flags for handling build tags, `-matrix` and `-merge`. Their use is extensively documented\non the new [Build tags]({{< relref \"/docs/running-staticcheck/cli/build-tags\" >}}) page. Their intended use is for\navoiding false positives when dealing with different build tags.\n\nNot tied directly to this release, but worth mentioning regardless: Staticcheck has an [official GitHub\nAction](https://github.com/dominikh/staticcheck-action) now, which may simplify your CI pipeline.\n\nMinor changes include slightly nicer output from `staticcheck -explain`, better error messages, and allowing whitespace in flags like `-checks`.\n\n## Checks {#checks}\n\n### New checks {#checks-new}\n\nThe following new checks have been added:\n\n- {{< check \"SA4028\" >}} flags `x % 1`, which always yields zero, and is sometimes accidentally used instead of `x % 2`\n- {{< check \"SA4029\" >}} flags misuses of `sort.IntSlice` and related types\n- {{< check \"SA4030\" >}} flags misuses of `math/rand` that always generate zeros\n- {{< check \"SA4031\" >}} flags comparisons of never-nil values against nil\n- {{< check \"SA9007\" >}} flags attempts at deleting system directories\n- {{< check \"SA9008\" >}} flags accidental shadowing in the else branches of type assertions\n\n### Changed checks {#checks-changed}\n\nThe following checks have been improved:\n\n- {{< check \"S1001\"  >}} now simplifies more loops\n- {{< check \"S1038\"  >}} now simplifies formatted printing in `log` and `testing`, in addition to `fmt`\n- {{< check \"SA1019\" >}} no longer flags deprecated API in the Go standard library if it doesn't know when the API was\n  deprecated. This is to avoid false positives when using older versions of Staticcheck on newer versions of Go, in\n  particular Go's `master` branch.\n- {{< check \"SA1020\" >}} no longer flags `net/http.ListenAndServe` with a completely empty address\n- {{< check \"ST1001\" >}} various packages of `github.com/mmcloughlin/avo` have been whitelisted by default\n- {{< check \"ST1008\" >}} no longer flags functions that return `(..., error, bool)`\n- {{< check \"ST1018\" >}} no longer flags emoji sequences\n- {{< check \"ST1023\" >}} no longer makes erroneous suggestions\n- Numerous checks have a better understanding of integer literals and can detect mistakes involving unconventional\n  literals such as `---1` instead of `-1`\n- Some runtime crashes have been fixed\n\n## Staticcheck 2022.1.1 release notes {#2022.1.1}\n\nThis release addresses the following false positives, crashes, and infinite loops:\n\n- {{< check \"SA1026\" >}} and {{< check \"SA5008\" >}} no longer get stuck in infinite loops when code attempts to marshal cyclic pointer types ({{< issue \"1202\" >}})\n- U1000 no longer crashes when code contains mutually recursive type instantiations ({{< issue \"1247\" >}})\n- U1000 no longer crashes when generic functions use composite literals of type parameter types ({{< commit \"0ccdb5c9dad7e96a8e3a3136738192491b37dbdb\" >}})\n- {{< check \"ST1021\" >}} now understands type names that are also English articles ({{< issue \"1187\" >}})\n- {{< check \"SA4023\" >}} no longer gets confused by the nilness of type parameters ({{< issue \"1242\" >}})\n- Some checks no longer crash when trying to generate automated code fixes that involve function literals ({{< issue \"1134\" >}})\n- {{< check \"SA1026\" >}} no longer claims that `encoding/json` cannot marshal generic maps ([golang/go#52467](https://golang.org/issue/52467))\n- The `binary` format has been improved to handle OS-specific file paths correctly, in turn making the `-merge` flag work more reliably ({{< commit \"1846305a946b13d350894512c7ac1e5ed71dc331\" >}})\n- When using the `-merge` or `-matrix` flags, diagnostics reported by {{< check \"SA4008\" >}} now have to occur in all runs to be reported, reducing the number of false positives ({{< commit \"0e678cbe1c8b3f09ac481673453886b1afc9906a\" >}})\n- U1000 now understands struct type conversions involving type parameters, reducing the number of false positives ({{< commit \"90804df0287d9265e565bcabbe19568efbe374fa\" >}})\n\n## Staticcheck 2022.1.2 release notes {#2022.1.2}\n\nThis release addresses the following false positives, crashes, infinite loops, and performance issues:\n\n- For certain packages that contain tens of thousands of types and methods, such as those generated by\n  [ygot](https://github.com/openconfig/ygot), Staticcheck now finishes [much\n  faster](https://github.com/openconfig/featureprofiles/pull/181#issuecomment-1119250596).\n- Several infinite loops when handling recursive type parameters have been fixed\n- {{< check \"S1009\" >}} no longer mistakes user-defined functions named `len` for the builtin ({{< issue \"1181\" >}})\n- {{< check \"ST1015\" >}} no longer reorders `switch` statements if their order is significant due to the use of `fallthrough` ({{< issue \"1188\" >}})\n- {{< check \"SA1013\" >}} now detects constants more robustly, avoiding both false negatives and false positives.\n  Furthermore, it makes sure that offending methods implement io.Seeker and doesn't just rely on the name `Seek` ({{< issue \"1213\" >}}).\n- {{< check \"SA5008\" >}} now understands more third-party extensions to `json` struct tags\n- A crash involving functions named `_` has been fixed ({{< issue \"1268\" >}})\n- A crash involving slicing type parameters of type `string | []byte` has been fixed ({{< issue \"1270\" >}})\n- {{< check \"SA1019\" >}} now handles imports of deprecated standard library packages in the same way it handles other\n  deprecated API, taking the targeted Go version into consideration ({{< issue \"1117\" >}})\n\nAdditionally it is strongly recommended to use Go 1.18.2 for building Staticcheck, as it fixes further generics-related\nbugs in the type checker.\n\n## Staticcheck 2022.1.3 release notes {#2022.1.3}\n\nThis release addresses the following issues:\n\n- Staticcheck maintains a cache to speed up repeated runs. This cache needs to be pruned regularly to keep its size in\n  check. This is meant to happen automatically, but it hasn't since Staticcheck 2020.2. This release corrects that ({{<\n  issue \"1283\" >}}.)\n- Some type sets containing both bidirectional and unidirectional channels would lead to panics ({{< issue \"1304\" >}})\n"
  },
  {
    "path": "website/content/changes/2023.1.md",
    "content": "---\ntitle: Staticcheck 2023.1 release notes\nlinkTitle: \"2023.1 (v0.4.0)\"\nweight: -8\n---\n\nStaticcheck 2023.1 adds support for Go 1.20, brings minor improvements to various checks, and replaces U1000\nwith a new implementation.\n\n## Checks {#checks}\n\n### Changed checks {#checks-changed}\n\nThe following checks have been improved:\n\n- The wording of {{< check \"S1001\" >}} has been made clearer for cases involving arrays. Furthermore, it no longer\n  suggests using `copy` when the function has been shadowed.\n- {{< check \"S1011\" >}} now recognizes index-based loops ({{< issue \"881\" >}}).\n- {{< check \"SA1019\" >}} no longer flags tests (internal or external) that use deprecated API from the package under\n  test ({{< issue \"1285\" >}}). Furthermore, entire declaration groups (such as groups of constants) can now be marked as\n  deprecated ({{< issue \"1313\" >}}).\n- {{< check \"SA4017\" >}} now detects more functions, including those in the `time` package ({{< issue \"1353\" >}}). \n  Additionally, its wording has been made clearer.\n- {{< check \"SA5010\" >}} no longer gets confused by type assertions involving generic types ({{< issue \"1354\" >}}).\n- {{< check \"ST1005\" >}} no longer flags errors that start with alpha-numeric acronyms such as `P384`.\n- Improvements to our intermediate representation may allow various checks to find more problems.\n\nStaticcheck now knows about version 2 of the `k8s.io/klog` package, in particular which functions abort control flow\n({{< issue \"1307\" >}}).\n\nIn addition to these minor improvements, U1000 has been rewritten from the ground up, operating on a\nprogram representation more suited to the task. In practice this means that there will be fewer false positives and more\ntrue positives.\n\nOverall, the rewrite fixes at least eight known bugs, both ones that have been a nuisance for a while,\nas well as ones newly introduced by generics\n({{< issue \"507\" >}}, {{< issue \"633\" >}}, {{< issue \"810\" >}}, {{< issue \"812\" >}}, {{< issue \"1199\" >}}, {{< issue\n\"1249\" >}}, {{< issue \"1282\" >}}, {{< issue \"1333\" >}}).\n\n## Staticcheck 2023.1.1 release notes {#2023.1.1}\n\nThis release fixes a crash, a false positive in U1000 ({{< issue \"1360\" >}}) and improves the way deprecated API is\nflagged ({{< issue \"1318\" >}}).\n\nWhen targeting a Go version that is older than the version that deprecated an API, {{< check \"SA1019\" >}} will no longer\nflag the use even if there is already an alternative available in the targeted Go version.\n\nFor example, `math/rand.Seed` has been deprecated in Go 1.20, but an alternative has existed since Go 1.0. In the past,\nwe would flag uses of `Seed` even if targeting e.g. Go 1.19, to encourage better forwards compatibility. This can lead\nto unnecessary churn, however, because the correct change may depend on the Go version in use. For example, for `Seed`\nbefore Go 1.20, the alternative is to use a separate instance of `math/rand.Rand`, whereas in Go 1.20, a possible\nalternative is to simply drop the call to `Seed`.\n\n## Staticcheck 2023.1.2 release notes {#2023.1.2}\n\nThis release fixes a bug that prevented the `binary` formatter from working ({{< issue \"1370\" >}}).\n\n## Staticcheck 2023.1.3 release notes {#2023.1.3}\n\nThis release fixes the following bugs:\n\n- A crash when embedding type aliases of unnamed types ({{< issue \"1361\" >}})\n- A false positive in U1000, claiming that type aliases are unused ({{< issue \"1365\" >}})\n- A bug in the `binary` formatter that prevented correct merging behavior for some checks ({{< issue \"1372\" >}})\n\n## Staticcheck 2023.1.4 release notes {#2023.1.4}\n\nThis release **adds support for Go 1.21** and fixes the following bugs:\n\n- Three crashes when encountering unnecessarily parenthesized statements ({{< issue \"1393\" >}}, {{< issue \"1400\" >}})\n- Unnecessarily high memory usage when analyzing composite literals such as `[]int{1<<31: 1}` ({{< issue \"1393\" >}})\n- A false positive in {{< check \"S1011\" >}} when appending to a dynamic left-hand side ({{< issue \"1399\" >}})\n- A crash involving generics ({{< issue \"1410\" >}})\n- A false positive in {{< check \"SA9001\" >}} involving control flow statements ({{< issue \"488\" >}})\n- A false positive in {{< check \"ST1003\" >}}, complaining about the names of fuzz functions ({{< issue \"1420\" >}}))\n\n## Staticcheck 2023.1.5 release notes {#2023.1.5}\n\nThis release fixes the following bug:\n\n- A crash involving methods named `_`\n\n## Staticcheck 2023.1.6 release notes {#2023.1.6}\n\nThis release fixes the following bugs:\n\n- A crash when using the upcoming Go 1.22 ({{< issue \"1442\" >}})\n- A false positive in {{< check \"SA9005\" >}} when embedding basic types ({{< issue \"1443\" >}})\n\n## Staticcheck 2023.1.7 release notes {#2023.1.7}\n\nThis release fixes some minor issues in Staticcheck's intermediate representation. Furthermore, it improves the way {{<\ncheck \"QF1003\" >}} generates suggested fixes, working around constraints in the language server protocol.\n\nThe released binaries for this version have been built with Go 1.22 and should no longer panic when checking code\ntargeting Go 1.22.\n"
  },
  {
    "path": "website/content/changes/2024.1.md",
    "content": "---\ntitle: Staticcheck 2024.1 release notes\nlinkTitle: \"2024.1 (v0.5.0)\"\nweight: -9\n---\n\n## Backwards incompatible changes\n\nStaticcheck 2024.1 contains the following backwards incompatible changes:\n\n- The `keyify` utility has been removed. The recommended alternative is gopls.\n- `staticcheck -merge` now exits with a non-zero status if any problems have\n  been found.\n\n## Improved Go 1.22 support\n\nThis release updates Staticcheck's database of deprecated standard library APIs\nto cover the Go 1.22 release. Furthermore, checks have been updated to correctly\nhandle the new \"for\" loop variable scoping behavior as well as ranging over\nintegers.\n\n## Added Go 1.23 support\n\nStaticcheck 2024.1 has full support for iterators / range-over-func.\nFurthermore, {{< check \"SA1015\" >}} will skip any code targeting Go 1.23 or\nnewer, as it is now possible to use `time.Tick` without leaking memory.\n\n## Improved handling of Go versions\n\nGo 1.21 more rigorously defined the meaning of the `go` directive in `go.mod`\nfiles, as well as its interactions with `//go:build go1.N` build constraints.\nThe `go` directive now specifies a _minimum_ Go version for the module.\nFurthermore, it sets the language version that is in effect, which may change\nthe semantics of Go code. For example, before Go 1.22, loop variables were\nreused across iterations, but since Go 1.22, loop variables only exist for the\nduration of an iteration. Modules that specify `go 1.22` will use the new\nsemantics, while modules that specify an older version will not.\n\nIndividual files can both upgrade and downgrade their language version by using\n`//go:build go1.N` directives. In a module that requires Go 1.22, a file\nspecifying Go 1.21 will experience the old loop variable semantics, and vice\nversa. Because the Go module as a whole still specifies a minimum version, even\nfiles specifying an older version will have access to the standard library of\nthe minimum version.\n\nStaticcheck 2024.1 takes all of this into consideration when analyzing the\nbehavior of Go code, when determining which checks are applicable, and when\nmaking suggestions. Older versions of Staticcheck were already aware of Go\nversions, but 2024.1 works on a more fine-grained, per-file basis, and\ndifferentiates between the pre- and post-1.21 semantics of the `go` directive.\n\nThe `-go` command line flag continues to exist. It will override any\nmodule-based version selection. This is primarily useful for Go modules that\ntarget older Go versions (because here, the `go` directive didn't specify a\n_minimum_ version), or when working outside of Go modules.\n\nTo prevent misinterpreting code, Staticcheck now refuses to analyze modules that\nrequire a version of Go that is newer than that with which Staticcheck was\nbuilt.\n\n## Checks\n\n### New checks\n\nThe following checks have been added:\n\n- {{< check \"SA1031\" >}} flags overlapping destination and source slices passed\n  to certain encoding functions.\n- {{< check \"SA1032\" >}} flags calls to\n  [`errors.Is`](https://pkg.go.dev/errors#Is) where the two arguments have been\n  swapped.\n- {{< check \"SA4032\" >}} flags impossible comparisons of\n  [runtime.GOOS](https://pkg.go.dev/runtime#GOOS) and\n  [runtime.GOARCH](https://pkg.go.dev/runtime#GOARCH) based on the file's build\n  tags.\n- {{< check \"SA6006\" >}} flags `io.WriteString(w, string(b))` as it would be\n  both simpler and more efficient to use `w.Write(b)`.\n- {{< check \"SA9009\" >}} flags comments that look like they intend to be\n  compiler directives but which aren't due to extraneous whitespace.\n\n### Changed checks\n\nThe following checks have been improved:\n\n- {{< check \"QF1001\" >}} no longer panics on expressions involving \"key: value\"\n  pairs ({{< issue \"1484\" >}}).\n- {{< check \"S1008\" >}} now understands that some built-in functions never\n  return negative values. For example, it now negates `len(x) > 0` as `len(x) ==\n  0` ({{< issue \"1422\" >}}).\n- {{< check \"S1009\" >}} now flags unnecessary nil checks that involve selector\n  expressions ({{< issue \"1527\" >}}).\n- {{< check \"S1017\" >}} no longer flags `if else` branches ({{< issue \"1447\"\n  >}}).\n- {{< check \"SA1006\" >}} now detects more Printf-like functions from the\n  standard library ({{< issue \"1528\" >}}).\n- {{< check \"SA1015\" >}} now skips any code targeting Go 1.23 or newer ({{<\n  issue \"1558\" >}}).\n- {{< check \"SA1029\" >}} now flags uses of the empty struct (`struct{}`) as\n  context keys ({{< issue \"1504\" >}}).\n- {{< check \"SA4003\" >}} now flags pointless integer comparisons that involve\n  literals, not just constants from the `math` package ({{< issue \"1470\" >}}).\n- {{< check \"SA4015\" >}} now supports conversions that involve generics.\n- {{< check \"SA4023\" >}} no longer panics on type sets that contain arrays ({{<\n  issue \"1397\" >}}).\n- {{< check \"SA5001\" >}} now emits a clearer message ({{< issue \"1489\" >}}).\n- {{< check \"SA9003\" >}} has been disabled by default because of too many noisy\n  positives ({{< issue \"321\" >}}).\n- {{< check \"ST1000\" >}} now permits punctuation following the package name, as\n  in \"Package pkg, which ...\" ({{< issue \"1452\" >}}).\n- {{< check \"ST1018\" >}} now accepts variation selectors in emoji and certain\n  Arabic formatting characters in string literals ({{< issue \"1456\" >}}).\n- {{< check \"ST1020\" >}} no longer flags comments that start with a deprecation\n  notice ({{< issue \"1378\" >}}).\n- {{< check \"U1000\" >}} handles generic interfaces slightly better, reducing the\n  number of false positives.\n- Due to improvements in the intermediate representation, various checks may now\n  detect more problems.\n\n## Miscellaneous changes and fixes\n\n- The `keyify` utility has been deleted. This functionality is provided by gopls\n  nowadays.\n- `staticcheck -merge` now exits with a non-zero exit status if any problems\n  were found. This matches the behavior of non-merge uses.\n- Malformed `staticcheck.conf` files now cause more useful errors to be emitted.\n- Labeled statements with blank labels no longer cause panics.\n- Functions with named return parameters that never actually return no longer\n  cause panics ({{< issue \"1533\" >}}).\n\n## Staticcheck 2024.1.1 release notes {#2024.1.1}\n\nThis release fixes the detection of the used Go version when Go was compiled\nwith experimental features such as `rangefunc` or `boringcrypto` ({{< issue\n\"1586\" >}}).\n"
  },
  {
    "path": "website/content/changes/2025.1.md",
    "content": "---\ntitle: Staticcheck 2025.1 release notes\nlinkTitle: \"2025.1 (v0.6.0)\"\nweight: -10\n---\n\n## Added Go 1.24 support\n\nThis release adds support for Go 1.24.\n\n## Checks\n\n### Changed checks\n\nThe following checks have been improved:\n\n- {{< check \"U1000\" >}} treats all fields in a struct as used if the struct has\n  a field of type `structs.HostLayout`.\n- {{< check \"S1009\" >}} now emits a clearer message.\n- {{< check \"S1008\" >}} no longer recommends simplifying branches that contain\n  comments ({{< issue \"704\" >}}, {{< issue \"1488\" >}}).\n- {{< check \"S1009\" >}} now flags another redundant nil check ({{< issue \"1605\" >}}).\n- {{< check \"QF1002\" >}} now emits a valid automatic fix for switches that use\n  initialization statements ({{< issue \"1613\" >}}).\n\n## Staticcheck 2025.1.1 release notes {#2025.1.1}\n\nThis is a re-release of 2025.1 but with prebuilt binaries that have been built\nwith Go 1.24.1.\n"
  },
  {
    "path": "website/content/changes/2026.1.md",
    "content": "---\ntitle: Staticcheck 2026.1 release notes\nlinkTitle: \"2026.1 (v0.7.0)\"\nweight: -11\n---\n\n## Improved Go 1.25 and Go 1.26 support\n\nThis release updates Staticcheck’s database of deprecated standard library APIs\nto cover the Go 1.25 and Go 1.26 releases, as well as to add some\n`crypto/elliptic` deprecations from Go 1.21 that were missing. Furthermore, it\nadds support for `new(expr)`, which was added in Go 1.26.\n\n## Other changes\n\n- Version mismatch checks have been relaxed and no longer care about mismatches\n  in the patch level. For example, Staticcheck built with Go 1.26.0 will be able\n  to check code using Go 1.26.1.\n- Staticcheck no longer opens `staticcheck.conf` files that aren't regular files\n  (or symlinks to regular files). See [this gomodfs\n  issue](https://github.com/tailscale/gomodfs/issues/17) for the motivation\n  behind this change.\n- Staticcheck now exits with a non-zero status code if it encountered an\n  invalid configuration file.\n\n## Checks\n\n### Changed checks\n\nThe following checks have been improved:\n\n- {{< check \"SA1026\" >}} no longer panics when checking code that tries to\n  marshal named functions ({{< issue \"1660\" >}}).\n- {{< check \"SA4000\" >}} no longer flags `var _ = T{} == T{}`, a pattern used to\n  ensure that type `T` is comparable ({{< issue \"1670\" >}}).\n- {{< check \"SA4000\" >}} now correctly skips structs containing floats.\n- {{< check \"SA4000\" >}} now skips functions from the `math/rand/v2` package.\n- {{< check \"SA4003\" >}} now skips over generated files.\n- {{< check \"SA4030\" >}} now also checks uses of `math/rand/v2`.\n- {{< check \"SA5008\" >}} has been updated with better support for `encoding/json/v2`.\n- {{< check \"SA5010\" >}} no longer tries to reason about generics, to avoid\n  false positives.\n- {{< check \"ST1019\" >}} no longer flags duplicate imports of unsafe, mainly to\n  play nice with cgo.\n- {{< check \"ST1003\" >}} and {{< check \"QF1002\" >}} now emit more concise\n  positions, benefitting users of gopls ({{< issue 1647 >}}).\n- {{< check \"ST1019\" >}} now allows importing the same package twice, once using\n  a blank import ({{< issue \"1688\" >}}).\n- {{< check \"QF1008\" >}} no longer offers to delete all embedded fields from a\n  selector expression. Even when two fields are individually superfluous,\n  removing both might change the semantics of the code ({{< issue \"1682\" >}}).\n- {{< check \"QF1012\" >}} now detects more uses of `bytes.Buffer` ({{< issue \"1097\" >}}).\n- A bug in the intermediate representation was fixed, affecting the behavior of\n  various checks ({{< issue \"1654\" >}}).\n"
  },
  {
    "path": "website/content/changes/_index.md",
    "content": "---\ntitle: Release notes\ntype: docs\noutputs:\n  - html\n  - RSS\ncascade:\n  type: docs\n---\n\n"
  },
  {
    "path": "website/content/contact.md",
    "content": "---\ntitle: \"Contact\"\nmenu:\n  main:\n    weight: 3\n    pre: <i class='fas fa-envelope'></i>\n---\n\n{{< blocks/section type=\"section\" color=\"white\"  >}}\n\nPlease contact me at dominik@honnef.co should you have any questions regarding this website or the services offered.\n\nThe following information are provided to comply with § 5 Telemediengesetz.\n\nDominik Honnef<br />\nBahnhofstraße 65<br />\n45770 Marl<br />\nGermany<br />\n<br />\nEmail: dominik@honnef.co<br />\nPhone: +49 151 178 067 90<br />\n<br />\nUSt-IdNr: DE298968211\n\n{{< /blocks/section >}}\n"
  },
  {
    "path": "website/content/docs/_index.md",
    "content": "---\ntitle: \"Welcome to Staticcheck\"\nlinkTitle: \"Documentation\"\nmenu:\n  main:\n    weight: 1\n    pre: <i class='fas fa-book'></i>\n---\n\nStaticcheck is a state of the art linter for the [Go programming language](https://go.dev/).\nUsing static analysis, it finds bugs and performance issues, offers simplifications, and enforces style rules.\n\n\nEach of the\n[\n{{< numchecks.inline >}}\n{{- $c := len $.Site.Data.checks.Checks -}}\n{{- sub $c (mod $c 25) -}}\n{{< /numchecks.inline >}}+\n]({{< relref \"/docs/checks\" >}}) checks has been designed to be fast, precise and useful.\nWhen Staticcheck flags code, you can be sure that it isn't wasting your time with unactionable warnings.\nUnlike many other linters, Staticcheck focuses on checks that produce few to no false positives.\nIt's the ideal candidate for running in CI without risking spurious failures.\n\nStaticcheck aims to be trivial to adopt.\nIt behaves just like the official `go` tool and requires no learning to get started with.\nJust run `staticcheck ./...` on your code in addition to `go vet ./...`.\n\nWhile checks have been designed to be useful out of the box,\nthey still provide [configuration]({{< relref \"/docs/configuration\" >}}) where necessary, to fine-tune to your needs, without overwhelming you with hundreds of options.\n\nStaticcheck can be used from the command line, in CI,\nand even [directly from your editor](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#staticcheck-bool).\n\n\nStaticcheck is open source and offered completely free of charge. [Sponsors]({{< relref \"/sponsors\" >}}) guarantee its continued development.\n\n<link rel=\"prefetch\" href=\"{{< relref \"/docs/getting-started\" >}}\">\n"
  },
  {
    "path": "website/content/docs/changes.md",
    "content": "---\ntitle: Release notes\nmanualLinkRelref: \"/changes\"\n---\n"
  },
  {
    "path": "website/content/docs/checks.html",
    "content": "---\ntitle: \"Checks\"\ndescription: \"Explanations for all checks in Staticcheck\"\nmenu:\n  main:\n    weight: 2\n    pre: <i class='fas fa-tasks'></i>\n---\n{{< all-checks.inline >}}\n{{ define \"category-list\" }}\n{{ range $name := index $.p.Site.Data.checks.ByCategory $.cat }}\n{{ $check := index $.p.Site.Data.checks.Checks $name }}\n<tr>\n\t<td><a href=\"#{{ $name }}\">{{ $name }}</a></td>\n\t<td>{{ $check.TitleMarkdown | markdownify }}</td>\n</tr>\n{{ end }}\n{{ end }}\n\n\n{{ define \"category\" }}\n{{ range $name := index $.p.Site.Data.checks.ByCategory .cat }}\n{{ $check := index $.p.Site.Data.checks.Checks $name }}\n<h4 id=\"{{ $name }}\">{{ $name }} - {{ $check.TitleMarkdown | markdownify }}{{ if $check.NonDefault }} <span class=\"badge badge-primary\">non-default</span>{{ end }}</h4>\n{{ $check.TextMarkdown | $.p.Page.RenderString (dict \"display\" \"block\") }}\n\n{{ if $check.Before }}\n<p class=\"before-after\"><strong>Before:</strong></p>\n{{ highlight $check.Before \"go\" \"\" }}\n{{ end }}\n\n{{ if $check.After }}\n<p class=\"before-after\"><strong>After:</strong></p>\n{{ highlight $check.After \"go\" \"\" }}\n{{ end }}\n\n\n<dl>\n  <dt>Available since</dt>\n  <dd>{{ $check.Since }}</dd>\n\n  {{ if $check.Options }}\n  <dt>Options</dt>\n  <dd>\n\t<ul>\n\t  {{ range $opt := $check.Options -}}\n\t  <li><a href=\"{{ relref $.p (printf \"/docs/configuration/options#%s\" $opt) }}\">{{ $opt  }}</a></li>\n\t  {{ end }}\n\t</ul>\n  </dd>\n  {{ end }}\n</dl>\n{{ end }}\n{{ end }}\n\n{{ $categoryNames := slice \"SA\" \"SA1\" \"SA2\" \"SA3\" \"SA4\" \"SA5\" \"SA6\" \"SA9\" \"S\" \"S1\" \"ST\" \"ST1\" \"QF\" \"QF1\" }}\n{{ $categories := dict\n  \"SA\"  (dict \"name\" \"SA\"  \"title\" \"<code>staticcheck</code>\")\n  \"SA1\" (dict \"name\" \"SA1\" \"title\" \"Various misuses of the standard library\")\n  \"SA2\" (dict \"name\" \"SA2\" \"title\" \"Concurrency issues\")\n  \"SA3\" (dict \"name\" \"SA3\" \"title\" \"Testing issues\")\n  \"SA4\" (dict \"name\" \"SA4\" \"title\" \"Code that isn't really doing anything\")\n  \"SA5\" (dict \"name\" \"SA5\" \"title\" \"Correctness issues\")\n  \"SA6\" (dict \"name\" \"SA6\" \"title\" \"Performance issues\")\n  \"SA9\" (dict \"name\" \"SA9\" \"title\" \"Dubious code constructs that have a high probability of being wrong\")\n\n  \"S\"   (dict \"name\" \"S\" \"title\" \"<code>simple</code>\")\n  \"S1\"  (dict \"name\" \"S1\" \"title\" \"Code simplifications\")\n\n  \"ST\"  (dict \"name\" \"ST\" \"title\" \"<code>stylecheck</code>\")\n  \"ST1\" (dict \"name\" \"ST1\" \"title\" \"Stylistic issues\")\n\n  \"QF\"  (dict \"name\" \"QF\" \"title\" \"<code>quickfix</code>\")\n  \"QF1\" (dict \"name\" \"QF1\" \"title\" \"Quickfixes\")\n}}\n\n<table>\n  <thead>\n\t<tr>\n\t  <th>Check</th>\n\t  <th>Short description</th>\n\t</tr>\n  </thead>\n\n  <tbody>\n  {{ range $name := $categoryNames }}\n  {{ $cat := index $categories $name }}\n\t<tr><th><a href=\"#{{ $name }}\">{{ $cat.name }}</a></th><th>{{ $cat.title }}</th></tr>\n\t{{ template \"category-list\" (dict \"p\" $ \"cat\" $cat.name) }}\n  {{ end }}\n  </tbody>\n</tr>\n</table>\n\n{{ define \"category-header\" }}\n<h2 id=\"{{ .name }}\">{{ .name }} – {{ .title }}</h2>\n{{ end }}\n\n{{ define \"subcategory-header\" }}\n<h3 id=\"{{ .name }}\">{{ .name }} – {{ .title }}</h3>\n{{ end }}\n\n{{ template \"category-header\" (index $categories \"SA\") }}\n<p>\n  The SA category of checks, codenamed <code>staticcheck</code>, contains all checks that are concerned with the correctness of code.\n</p>\n{{ template \"subcategory-header\" (index $categories \"SA1\") }}\n<p>\n  Checks in this category deal with misuses of the standard library.\n  This tends to involve incorrect function arguments\n  or violating other invariants laid out by the standard library's documentation.\n</p>\n{{ template \"category\" (dict \"p\" $ \"cat\" \"SA1\") }}\n\n\n{{ template \"subcategory-header\" (index $categories \"SA2\") }}\n<p>\n  Checks in this category find concurrency bugs.\n</p>\n{{ template \"category\" (dict \"p\" $ \"cat\" \"SA2\") }}\n\n\n{{ template \"subcategory-header\" (index $categories \"SA3\") }}\n<p>\n  Checks in this category find issues in tests and benchmarks.\n</p>\n{{ template \"category\" (dict \"p\" $ \"cat\" \"SA3\") }}\n\n\n{{ template \"subcategory-header\" (index $categories \"SA4\") }}\n<p>\n  Checks in this category point out code that doesn't have any meaningful effect on a program's execution.\n  Usually this means that the programmer thought the code would do one thing while in reality it does something else.\n</p>\n{{ template \"category\" (dict \"p\" $ \"cat\" \"SA4\") }}\n\n\n{{ template \"subcategory-header\" (index $categories \"SA5\") }}\n<p>\n  Checks in this category find assorted bugs and crashes.\n</p>\n{{ template \"category\" (dict \"p\" $ \"cat\" \"SA5\") }}\n\n\n{{ template \"subcategory-header\" (index $categories \"SA6\") }}\n<p>\n  Checks in this category find code that can be trivially made faster.\n</p>\n{{ template \"category\" (dict \"p\" $ \"cat\" \"SA6\") }}\n\n\n{{ template \"subcategory-header\" (index $categories \"SA9\") }}\n<p>\n  Checks in this category find code that is probably wrong.\n  Unlike checks in the other <code>SA</code> categories,\n  checks in <code>SA9</code> have a slight chance of reporting false positives.\n  However, even false positives will point at code that is confusing and that should probably be refactored.\n</p>\n{{ template \"category\" (dict \"p\" $ \"cat\" \"SA9\") }}\n\n{{ template \"category-header\" (index $categories \"S\") }}\n<p>\n  The S category of checks, codenamed <code>simple</code>, contains all checks that are concerned with simplifying code.\n</p>\n\n{{ template \"subcategory-header\" (index $categories \"S1\") }}\n<p>\n  Checks in this category find code that is unnecessarily complex and that can be trivially simplified.\n</p>\n{{ template \"category\" (dict \"p\" $ \"cat\" \"S1\") }}\n\n{{ template \"category-header\" (index $categories \"ST\") }}\n<p>\n  The ST category of checks, codenamed <code>stylecheck</code>, contains all checks that are concerned with stylistic issues.\n</p>\n\n{{ template \"subcategory-header\" (index $categories \"ST1\") }}\n<p>\n  The rules contained in this category are primarily derived from the <a href=\"https://go.dev/wiki/CodeReviewComments\">Go wiki</a> and represent community consensus.\n</p>\n\n<p>\n  Some checks are very pedantic and disabled by default.\n  You may want to <a href=\"{{ relref . \"/docs/configuration/options#checks\" }}\">tweak which checks from this category run</a>, based on your project's needs.\n</p>\n{{ template \"category\" (dict \"p\" $ \"cat\" \"ST1\") }}\n\n{{ template \"category-header\" (index $categories \"QF\") }}\n<p>\n  The QF category of checks, codenamed <code>quickfix</code>, contains checks that are used as part of <em>gopls</em> for automatic refactorings.\n  In the context of gopls, diagnostics of these checks will usually show up as hints, sometimes as information-level diagnostics.\n</p>\n{{ template \"subcategory-header\" (index $categories \"QF1\") }}\n{{ template \"category\" (dict \"p\" $ \"cat\" \"QF1\") }}\n\n{{< /all-checks.inline >}}\n"
  },
  {
    "path": "website/content/docs/configuration/_index.md",
    "content": "---\ntitle: \"Configuration\"\ndescription: \"Tweak Staticcheck to your requirements\"\nweight: 3\n---\n\nStaticcheck tries to provide a good out-of-the-box experience, but it also offers a number of options to fine-tune it to your specific needs.\n\n## Command-line flags {#cli-flags}\n\nStaticcheck uses command-line flags for settings that are specific to single invocations and that may change depending on the context (local development, continuous integration etc).\nThis includes which build tags to set and which output format to use.\nAll of the CLI flags are explained in the [Command-line interface]({{< relref \"/docs/running-staticcheck/cli\" >}}) article.\n\n## Configuration files {#configuration-files}\n\nStaticcheck uses configuration files for settings that apply to all users of Staticcheck on a given project.\nConfiguration files can choose which checks to run as well as tweak the behavior of individual checks.\n\nConfiguration files are named `staticcheck.conf` and apply to subtrees of packages. Consider the following tree of Go packages and configuration files:\n\n```plain\n.\n├── net\n│   ├── cgi\n│   ├── http\n│   │   ├── parser\n│   │   └── staticcheck.conf // config 3\n│   └── staticcheck.conf     // config 2\n├── staticcheck.conf         // config 1\n└── strconv\n```\n\nConfig 1 will apply to all packages, config 2 will apply to `./net/...` and config 3 will apply to `./net/http/...`.\nWhen multiple configuration files apply to a package (for example, all three configs will apply to `./net/http`) they will be merged, with settings in files deeper in the package tree overriding rules higher up the tree.\n\n### Configuration format {#configuration-format}\n\nStaticcheck configuration files are named `staticcheck.conf` and contain [TOML](https://github.com/toml-lang/toml).\n\nAny set option will override the same option from further up the package tree,\nwhereas unset options will inherit their values.\nAdditionally, the special value `\"inherit\"` can be used to inherit values.\nThis is especially useful for array values, as it allows adding and removing values to the inherited option.\nFor example, the option `checks = [\"inherit\", \"ST1000\"]` will inherit the enabled checks and additionally enable ST1000.\n\nThe special value `\"all\"` matches all possible values.\nthis is used when enabling or disabling checks.\n\nValues prefixed with a minus sign,  such as `\"-S1000\"`  will exclude values from a list.\nThis can be used in combination with `\"all\"` to express \"all but\",\nor in combination with `\"inherit\"` to remove values from the inherited option.\n\n### Configuration options {#configuration-options}\n\nA list of all options and their explanations can be found on the [Options]({{< relref \"/docs/configuration/options\" >}}) page.\n\n### Example configuration {#example-configuration}\n\nThe following example configuration is the textual representation of Staticcheck's default configuration.\n\n{{% content \"default_config.md\" %}}\n\n## Ignoring problems with linter directives {#ignoring-problems}\n\nIn general, you shouldn't have to ignore problems reported by Staticcheck.\nGreat care is taken to minimize the number of false positives and subjective suggestions.\nDubious code should be rewritten and genuine false positives should be reported so that they can be fixed.\n\nThe reality of things, however, is that not all corner cases can be taken into consideration.\nSometimes code just has to look weird enough to confuse tools,\nand sometimes suggestions, though well-meant, just aren't applicable.\nFor those rare cases, there are several ways of ignoring unwanted problems.\n\n### Line-based linter directives {#line-based-linter-directives}\n\nThe most fine-grained way of ignoring reported problems is to annotate the offending lines of code with linter directives.\n\nThe `//lint:ignore Check1[,Check2,...,CheckN] reason` directive\nignores one or more checks on the following line of code.\nThe `reason` is a required field\nthat must describe why the checks should be ignored for that line of code.\nThis field acts as documentation for other people (including future you) reading the code.\n\nLet's consider the following example,\nwhich intentionally checks that the results of two identical function calls are not equal:\n\n```go\nfunc TestNewEqual(t *testing.T) {\n  if errors.New(\"abc\") == errors.New(\"abc\") {\n    t.Errorf(`New(\"abc\") == New(\"abc\")`)\n  }\n}\n```\n\n{{< check \"SA4000\" >}} will flag this code,\npointing out that the left and right side of `==` are identical –\nusually indicative of a typo and a bug.\n\nTo silence this problem, we can use a linter directive:\n\n```go\nfunc TestNewEqual(t *testing.T) {\n  //lint:ignore SA4000 we want to make sure that no two results of errors.New are ever the same\n  if errors.New(\"abc\") == errors.New(\"abc\") {\n    t.Errorf(`New(\"abc\") == New(\"abc\")`)\n  }\n}\n```\n\n### Maintenance of linter directives {#maintenance-of-linter-directives}\n\nIt is crucial to update or remove outdated linter directives when code has been changed.\nStaticcheck helps you with this by making unnecessary directives a problem of its own.\nFor example, for this (admittedly contrived) snippet of code\n\n```go\n//lint:ignore SA1000 we love invalid regular expressions!\nregexp.Compile(\".+\")\n```\n\nStaticcheck will report the following:\n\n```plain\ntmp.go:1:2: this linter directive didn't match anything; should it be removed?\n```\n\nChecks that have been disabled via configuration files will not cause directives to be considered unnecessary.\n\n### File-based linter directives {#file-based-linter-directives}\n\nIn some cases, you may want to disable checks for an entire file.\nFor example, code generation may leave behind a lot of unused code,\nas it simplifies the generation process.\nInstead of manually annotating every instance of unused code,\nthe code generator can inject a single, file-wide ignore directive to ignore the problem.\n\nFile-based linter directives look a lot like line-based ones:\n\n```go\n//lint:file-ignore U1000 Ignore all unused code, it's generated\n```\n\nThe only difference is that these comments aren't associated with any specific line of code.\nConventionally, these comments should be placed near the top of the file.\n\nUnlike line-based directives, file-based ones will not be flagged for being unnecessary.\n"
  },
  {
    "path": "website/content/docs/configuration/options.md",
    "content": "---\ntitle: \"Options\"\ndescription: \"Explanations for all options\"\naliases:\n  - /docs/options\n---\n\n## checks {#checks}\n\nThis option sets which [checks]({{< relref \"/docs/checks\" >}}) should be enabled.\nBy default, most checks will be enabled, except for those that are too opinionated or that only apply to packages in certain domains.\n\nAll supported checks can be enabled with `\"all\"`.\nSubsets of checks can be enabled via prefixes and the `*` glob; for example, `\"S*\"`, `\"SA*\"` and `\"SA1*\"` will\nenable all checks in the S, SA and SA1 subgroups respectively.\nIndividual checks can be enabled by their full IDs.\nTo disable checks, prefix them with a minus sign. This works on all of the previously mentioned values.\n\nDefault value: `[\"all\", \"-{{< check \"ST1000\" >}}\", \"-{{< check \"ST1003\" >}}\", \"-{{< check \"ST1016\" >}}\", \"-{{< check \"ST1020\" >}}\", \"-{{< check \"ST1021\" >}}\", \"-{{< check \"ST1022\" >}}\"]`\n\n## initialisms {#initialisms}\n\n{{< check \"ST1003\" >}} checks, among other\nthings, for the correct capitalization of initialisms. The\nset of known initialisms can be configured with this option.\n\nDefault value: `[\"ACL\", \"API\", \"ASCII\", \"CPU\", \"CSS\", \"DNS\", \"EOF\", \"GUID\", \"HTML\", \"HTTP\", \"HTTPS\", \"ID\", \"IP\", \"JSON\", \"QPS\", \"RAM\", \"RPC\", \"SLA\", \"SMTP\", \"SQL\", \"SSH\", \"TCP\", \"TLS\", \"TTL\", \"UDP\", \"UI\", \"GID\", \"UID\", \"UUID\", \"URI\", \"URL\", \"UTF8\", \"VM\", \"XML\", \"XMPP\", \"XSRF\", \"XSS\", \"SIP\", \"RTP\", \"AMQP\", \"DB\", \"TS\"]`\n\n## dot_import_whitelist {#dot_import_whitelist}\n\nBy default, {{< check \"ST1001\" >}} forbids\nall uses of dot imports in non-test packages. This\nsetting allows setting a whitelist of import paths that can\nbe dot-imported anywhere.\n\nDefault value: `[]`\n\n## http_status_code_whitelist {#http_status_code_whitelist}\n\n{{< check \"ST1013\" >}} recommends using constants from the `net/http` package\ninstead of hard-coding numeric HTTP status codes. This\nsetting specifies a list of numeric status codes that this\ncheck does not complain about.\n\nDefault value: `[\"200\", \"400\", \"404\", \"500\"]`\n"
  },
  {
    "path": "website/content/docs/faq.md",
    "content": "---\ntitle: \"Frequently Asked Questions\"\n---\n\n\n{{% faq/list %}}\n{{% faq/question id=\"false-positives\" question=\"Staticcheck is wrong, what should I do?\" %}}\nFirst, make sure that Staticcheck is actually wrong.\nIt can find very subtle bugs, and what may look like a false positive at first glance is usually a genuine bug.\nThere is a long list of competent programmers\n[who got it wrong before.](https://github.com/dominikh/go-tools/issues?q=is%3Aissue+label%3Afalse-positive+label%3Ainvalid+is%3Aclosed)\n\nHowever, sometimes Staticcheck _is_ wrong and you want to suppress a warning to get on with your work.\nIn that case, you can use [ignore directives to ignore specific problems]({{< relref \"/docs/configuration/#ignoring-problems\" >}}).\nYou should also [report the false positive](https://github.com/dominikh/go-tools/issues/new?assignees=&labels=false-positive%2C+needs-triage&template=1_false_positive.md&title=) so that we can fix it.\nWe don't expect users to have to ignore many problems, and we always aim to avoid false positives.\n\nSome checks, particularly those in the `ST` (stylecheck) category, may not be applicable to your code base at all. In that case, you should disable the check using the\n[`checks` option]({{< relref \"/docs/configuration/options#checks\" >}})\nin your [configuration]({{< relref \"/docs/configuration/#configuration-files\" >}}).\n{{% /faq/question %}}\n\n{{% faq/question id=\"go-version\" question=\"Staticcheck's suggestions don't apply to my version of Go\" %}}\nYou can [specify the version of Go your code should work with.]({{< relref \"/docs/configuration/#targeting-go-versions\" >}})\n{{% /faq/question %}}\n{{% /faq/list %}}\n"
  },
  {
    "path": "website/content/docs/getting-started.md",
    "content": "---\ntitle: \"Getting started\"\ndescription: \"Quickly get started using Staticcheck\"\nweight: 1\naliases:\n  - /docs/install\n---\n\n## Installation\n\nBeginning with Go 1.17, the simplest way of installing Staticcheck is by running:\n\n```\ngo install honnef.co/go/tools/cmd/staticcheck@latest\n```\n\nThis will install the latest version of Staticcheck to `$GOPATH/bin`. To find out where `$GOPATH` is, run `go env GOPATH`.\nInstead of `@latest`, you can also use a specific version, such as `@2020.2.1`.\n\nIf you'd like to be notified of new releases, you can use [GitHub's Releases only watches](https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/viewing-your-subscriptions#configuring-your-watch-settings-for-an-individual-repository).\n\n### Binary releases\n\nWe publish binary releases for the most common operating systems and CPU architectures.\nThese can be downloaded from [GitHub](https://github.com/dominikh/go-tools/releases).\n\n### Distribution packages\n\nMany package managers include Staticcheck, allowing you to install it with your usual commands, such as `apt install`.\nNote, however, that you might not always get new releases in a timely manner.\n\nWhat follows is a non-exhaustive list of the package names in various package repositories.\n\n<div id=\"getting-started-distribution-packages\">\n\nArch Linux\n: [staticcheck](https://archlinux.org/packages/extra/x86_64/staticcheck/)\n\nDebian\n: [go-staticcheck](https://packages.debian.org/go-staticcheck)\n\nFedora\n: [golang-honnef-tools](https://fedora.pkgs.org/33/fedora-x86_64/golang-honnef-tools-2020.1.5-2.fc33.x86_64.rpm.html)\n\nHomebrew\n: [staticcheck](https://formulae.brew.sh/formula/staticcheck)\n\nMacPorts\n: [staticcheck](https://ports.macports.org/port/staticcheck/summary)\n\nNixOS\n: go-tools\n\nScoop\n: [staticcheck](https://github.com/ScoopInstaller/Main/blob/master/bucket/staticcheck.json)\n\n</div>\n\n## Running Staticcheck\n\nThe `staticcheck` command works much like `go build` or `go vet` do.\nIt supports all of the same package patterns.\nFor example, `staticcheck .` will check the current package, and `staticcheck ./...` will check all packages.\nFor more details on specifying packages to check, see `go help packages`.\n\nTherefore, to start using Staticcheck, just run it on your code: `staticcheck ./...`.\nIt will print any issues it finds, or nothing at all if your code is squeaky clean.\n\nRead the [Running Staticcheck]({{< relref \"/docs/running-staticcheck\" >}}) articles to learn more about running Staticcheck.\n"
  },
  {
    "path": "website/content/docs/running-staticcheck/_index.md",
    "content": "---\ntitle: Running Staticcheck\nweight: 2\naliases:\n  - /docs/run\n---\n"
  },
  {
    "path": "website/content/docs/running-staticcheck/ci/_index.md",
    "content": "---\ntitle: Continuous integration\ndescription: How to run Staticcheck in CI\n---\n"
  },
  {
    "path": "website/content/docs/running-staticcheck/ci/github-actions/index.md",
    "content": "---\ntitle: GitHub Actions\ndescription: Running Staticcheck in GitHub Actions\naliases:\n  - /docs/running-staticcheck/github-actions\n---\nWe publish [our own action](https://github.com/marketplace/actions/staticcheck) for [GitHub Actions](https://github.com/features/actions),\nwhich makes it very simple to run Staticcheck in CI on GitHub.\n\n## Examples\n\nAt its simplest, just add `dominikh/staticcheck-action` as a step in your existing workflow.\nA minimal workflow might look like this:\n\n```yaml\nname: \"CI\"\non: [\"push\", \"pull_request\"]\n\njobs:\n  ci:\n\tname: \"Run CI\"\n\truns-on: ubuntu-latest\n\tsteps:\n\t- uses: actions/checkout@v1\n\t  with:\n\t\tfetch-depth: 1\n\t- uses: dominikh/staticcheck-action@v1.2.0\n\t  with:\n\t\tversion: \"2022.1.1\"\n```\n\nA more advanced example that runs tests, go vet and Staticcheck on multiple OSs and Go versions looks like this:\n\n```yaml\nname: \"CI\"\non: [\"push\", \"pull_request\"]\n\njobs:\n  ci:\n\tname: \"Run CI\"\n\tstrategy:\n\t  fail-fast: false\n\t  matrix:\n\t\tos: [\"windows-latest\", \"ubuntu-latest\", \"macOS-latest\"]\n\t\tgo: [\"1.16.x\", \"1.17.x\"]\n\truns-on: ${{ matrix.os }}\n\tsteps:\n\t- uses: actions/checkout@v1\n\t  with:\n\t\tfetch-depth: 1\n\t- uses: WillAbides/setup-go-faster@v1.7.0\n\t  with:\n\t\tgo-version: ${{ matrix.go }}\n\t- run: \"go test ./...\"\n\t- run: \"go vet ./...\"\n\t- uses: dominikh/staticcheck-action@v1.2.0\n\t  with:\n\t\tversion: \"2022.1.1\"\n\t\tinstall-go: false\n\t\tcache-key: ${{ matrix.go }}\n```\n\nNote that this example could benefit from further improvements, such as caching of Go's build cache.\n\n## Managing Go\n\nBy default, `staticcheck-action` installs Go so that it can install and run Staticcheck.\nIt also saves and restores Go's build cache (in addition to Staticcheck's own cache) to speed up future runs.\nThis is intended for trivial jobs that only run Staticcheck, not other steps such as `go test`.\n\nFor more complicated jobs, it is strongly recommended that you set `install-go` to `false`,\ninstall Go yourself (e.g. by using [`actions/setup-go`](https://github.com/actions/setup-go) or [`WillAbides/setup-go-faster`](https://github.com/WillAbides/setup-go-faster)),\nand save and restore the Go build cache, for improved performance.\n\nWhen installing Go, make sure the version meets Staticcheck's minimum requirements.\nA given Staticcheck release supports the last two versions of Go (such as Go 1.16 and Go 1.17) at the time of release.\nThe action itself requires at least Go 1.16.\n\n## Options\n\n### `version`\n\nWhich version of Staticcheck to use.\nBecause new versions of Staticcheck introduce new checks that may break your build,\nit is recommended to pin to a specific version and to update Staticheck consciously.\n\nIt defaults to `latest`, which installs the latest released version of Staticcheck.\n\n### `min-go-version`\n\nMinimum version of Go to support. This affects the diagnostics reported by Staticcheck,\navoiding suggestions that are not applicable to older versions of Go.\n\nIf unset, this will default to the Go version specified in your go.mod.\n\nSee https://staticcheck.dev/docs/running-staticcheck/cli/#go for more information.\n\n### `build-tags`\n\nGo build tags that get passed to Staticcheck via the `-tags` flag.\n\n### `install-go`\n\nWhether the action should install a suitable version of Go to install and run Staticcheck.\nIf Staticcheck is the only action in your job, this option can usually be left on its default value of `true`.\nIf your job already installs Go prior to running Staticcheck, for example to run unit tests, it is best to set this option to `false`.\n\nThe latest release of Staticcheck works with the last two minor releases of Go.\nThe action itself requires at least Go 1.16.\n\n### `cache-key`\n\nString to include in the cache key, in addition to the default, which is `runner.os`.\nThis is useful when using multiple Go versions.\n"
  },
  {
    "path": "website/content/docs/running-staticcheck/cli/_index.md",
    "content": "---\ntitle: Command-line interface\ndescription: How to use the `staticcheck` command\n---\nThe `staticcheck` command is the primary way of running Staticcheck.\n\nAt its core, the `staticcheck` command works a lot like `go vet` or `go build`.\nIt accepts the same package patterns (see `go help packages` for details),\nit outputs problems in the same format,\nit supports a `-tags` flag for specifying which build tags to use, and so on.\nOverall, it is meant to feel like another `go` command.\n\nHowever, it also comes with several of its own flags to support some of its unique functionality.\nThis article will focus on explaining that unique functionality.\n\n<!-- TODO -->\n<!-- ## Specifying which checks to run {#checks} -->\n\n## Explaining checks {#explain}\n\nYou can use `staticcheck -explain <check>` to get a helpful description of a check.\n\nEvery diagnostic that staticcheck reports is annotated with the identifier of the specific check that found the issue. For example, in\n\n```text\nfoo.go:1248:4: unnecessary use of fmt.Sprintf (S1039)\n```\n\nthe check's identifier is S1039. Running `staticcheck -explain S1039` will output the following:\n\n```text\nUnnecessary use of fmt.Sprint\n\nCalling fmt.Sprint with a single string argument is unnecessary and identical to using the string directly.\n\nAvailable since\n\t2020.1\n\nOnline documentation\n\thttps://staticcheck.dev/docs/checks#S1039\n```\n\nThe output includes a one-line summary, one or more paragraphs of helpful text, the first version of Staticcheck that the check appeared in, and a link to online documentation, which contains the same information as the output of `staticcheck -explain`.\n\n## Selecting an output format {#format}\n\nStaticcheck can format its output in a number of ways, by using the `-f` flag.\nSee this [list of formatters]({{< relref \"/docs/running-staticcheck/cli/formatters\" >}}) for a list of all formatters.\n\n<!-- TODO -->\n<!-- ## Controlling the exit status {#fail} -->\n\n## Targeting Go versions {#go}\n\nSome of Staticcheck's analyses adjust their behavior based on the targeted Go version.\nFor example, the suggestion that one use `for range xs` instead of `for _ = range xs` only applies to Go 1.4 and later, as it won't compile with versions of Go older than that.\n\nBy default, Staticcheck targets the Go version declared in `go.mod` via the `go` directive.\nFor Go 1.21 and newer, that directive specifies the minimum required version of Go.\n\nFor older versions of Go, the directive technically specifies the maximum version of language features that the module\ncan use, which means it might be higher than the minimum required version. In those cases, you can manually overwrite\nthe targeted Go version by using the `-go` command line flag. For example, `staticcheck -go 1.0 ./...` will only make\nsuggestions that work with Go 1.0.\n\nThe targeted Go version limits both language features and parts of the standard library that will be recommended.\n\n## Excluding tests {#tests}\n\nBy default, Staticcheck analyses packages as well as their tests.\nBy passing `-tests=false`, one can skip the analysis of tests.\nThis is primarily useful for the {{< check \"U1000\" >}} check, as it allows finding code that is only used by tests and would otherwise be unused.\n"
  },
  {
    "path": "website/content/docs/running-staticcheck/cli/build-tags/index.md",
    "content": "---\ntitle: Build tags\ndescription: How to correctly check code that uses build tags\n---\n\n## Introduction\n\nIn Go, files can have build tags, which control when said files will be part of a package.\nFor example, two files might contain alternate implementations of the same function, targeting Linux and Windows respectively.\n\nDue to this, a single import path really refers to a collection of packages, with any particular package being chosen by a combination of build tags.\nEven if your code doesn't make use of build tags, any of your transitive dependencies might.\nTherefore, running `staticcheck my/package` really only checks one variant of `my/package`.\n\nFor more information on Go's build tags, see [`go help buildconstraint`](https://pkg.go.dev/cmd/go#hdr-Build_constraints).\n\n## Implications\n\nChecking packages using a single set of build tags can lead to both false positives and false negatives.\nThe reason for false negatives is straightforward: if some code is being excluded by build tags, then we won't check it.\nFalse positives can be a bit more involved. Consider the following package, in [txtar format](https://pkg.go.dev/golang.org/x/tools/txtar#hdr-Txtar_format):\n\n```go\n-- api_linux.go --\npackage pkg\n\nfunc Entry() { foo() }\n-- api_windows.go --\npackage pkg\n\nfunc Entry() { bar() }\n-- shared.go --\npackage pkg\n\nfunc foo() {}\nfunc bar() {}\n```\n\nIf we don't check the Windows build, then the function `bar` seems unused.\nSimilarly, if we don't check the Linux build, `foo` seems unused.\n\n```terminal\n$ GOOS=linux staticcheck\nshared.go:4:6: func bar is unused (U1000)\n$ GOOS=windows staticcheck\nshared.go:3:6: func foo is unused (U1000)\n```\n\nOnly when we check both builds do we see that both functions are in fact used.\nArguably, `foo` and `bar` should live in files with matching build tags to avoid this.\nHowever, in reality, code bases can be complex, and it isn't always clear which sets of build tags make use of what code.\nAfter all, if we always knew, we wouldn't have dead code to begin with.\n\nAnother example involves control flow. Consider this package, again in txtar format:\n\n```go\n-- api_linux.go --\npackage pkg\n\nfunc dieIfUnsupported() { panic(\"unsupported\") }\n-- api_windows.go --\npackage pkg\n\nfunc dieIfUnsupported() {}\n-- shared.go --\npackage pkg\n\nfunc foo() {}\n\nfunc Entry() {\n\tdieIfUnsupported()\n\tfoo()\n}\n```\n\nHere, `dieIfUnsupported` panics unconditionally on Linux, but not on Windows.\nBecause Staticcheck takes control flow into consideration, this means that `foo` is unused on Linux but used on Windows.\n\nSeveral checks have this sort of false positive, not just U1000.\n\n## Solution\n\nThe solution to this problem is to run Staticcheck multiple times with different build tags and to merge the results.\n\nAt first glance, one might think that Staticcheck should be able to do this fully automatically: look at all build tags, find all unique combinations, and check them all.\nHowever, this doesn't scale.\nTo be correct, Staticcheck would have to take dependencies and their tags into consideration, too.\nVirtually all code depends on the Go standard library, and the Go standard library supports a plethora of operating systems, architectures, and a number of tags such as `netgo`.\nAll in all, there are thousands of unique combinations.\nChecking all of these would take far too long.\n\nHowever, the number of build configurations you care about is probably much smaller.\nYour software probably supports 2-3 operating systems on 1-2 architectures,\nand maybe has a debug and a release build.\nThis makes for a lot fewer combinations that need to be checked.\nThese are probably the same combinations you're already checking in CI, too, by running their tests.\nThis will become useful in a bit.\n\n### The `-merge` flag\n\nUsing the `-merge` flag, Staticcheck can merge the results of multiple runs.\nIt decides on a per-check basis whether any run or all runs have to have reported an issue for it to be valid.\nIt also takes into consideration which files were checked by which run, to reduce false negatives.\n\nIn order to use `-merge`, the runs to be merged have to use the `-f binary` flag.\nThis outputs results in a binary format containing all information required by `-merge`.\nWhen using `-merge`, arguments are interpreted as file names instead of import paths, so that `staticcheck -merge file1 file2` will read the files `file1` and `file2`, which must contain the output of `staticcheck -f binary` runs, and merge them.\n\n```terminal\n$ GOOS=linux staticcheck -f binary >file1\n$ GOOS=windows staticcheck -f binary >file2\n$ staticcheck -merge file1 file2\n...\n```\n\n\nAlternatively, if no arguments are passed, `staticcheck -merge` will read from standard input instead.\nThis allows for workflows like\n\n```\n(\n  GOOS=linux staticcheck -f binary\n  GOOS=windows staticcheck -f binary\n) | staticcheck -merge\n```\n\nThis multi-step workflow of generating per-run output and merging it makes it possible to run Staticcheck on different systems before merging the results, which might be especially required when using cgo.\n\n### The `-matrix` flag\n\nWith the `-matrix` flag, you can instruct Staticcheck to check multiple build configurations at once and merge the results.\nIn other words, it automates running Staticcheck multiple times and merging results afterwards.\nThis is useful when all configurations can be checked on a single system, for example because you don't use cgo.\n\nWhen using the `-matrix` flag, Staticcheck reads a build matrix from standard input.\nThe build matrix uses a line-based format, where each non-empty line specifies a build name, environment variables and command-line flags.\nA line is of the format `<name>: [environment variables] [flags]`, for example `linux_debug: GOOS=linux -tags=debug -some-flag=\"some value\"`.\nEnvironment variables and flags get passed to `go` when Staticcheck analyzes code, so you can use all flags that `go` supports, such as `-tags` or `-gcflags`, although few flags other than `-tags` are really useful.\nValid build names consist of letters, numbers and underscores.\n\nHere is an example of using a build matrix:\n\n```terminal\n$ staticcheck -matrix <<EOF\nwindows: GOOS=windows\nlinux: GOOS=linux\nappengine: GOOS=linux -tags=appengine\nEOF\n```\n\n```terminal\nroot_windows.go:292:47: syscall.StringToUTF16Ptr has been deprecated since Go 1.1: Use UTF16PtrFromString instead.  [windows] (SA1019)\nverify_test.go:1338:7: const issuerSubjectMatchRoot is unused [appengine,linux,windows] (U1000)\n```\n\nStaticcheck will annotate results with the names of build configurations under which they occurred.\n\nIt's possible to combine `-matrix` and `-merge` by using `-matrix -f binary` and merging the results of multiple matrix runs.\n"
  },
  {
    "path": "website/content/docs/running-staticcheck/cli/formatters.md",
    "content": "---\ntitle: \"Formatters\"\ndescription: \"Format Staticcheck's output in different ways\"\naliases:\n  - /docs/formatters\n---\n\n## Text {#text}\n\n_Text_ is the default output formatter.\nIt formats problems using the following format: `file:line:col: message`.\nThis format is commonly used by compilers and linters,\nand is understood by most editors.\n\n### Example output\n```text\ngo/src/fmt/print.go:1069:15: this value of afterIndex is never used (SA4006)\n```\n\n## Stylish {#stylish}\n\n_Stylish_ is a formatter designed for human consumption.\nIt groups results by file name\nand breaks up the various pieces of information into columns.\nAdditionally, it displays a final summary.\n\nThis output format is not suited for automatic consumption by tools\nand may change between versions.\n\n```text\ngo/src/fmt/fmt_test.go\n(43, 2)     S1021   should merge variable declaration with assignment on next line\n(1185, 10)  SA9003  empty branch\n\ngo/src/fmt/print.go\n(77, 18)    ST1006  methods on the same type should have the same receiver name (seen 3x \"b\", 1x \"bp\")\n(1069, 15)  SA4006  this value of afterIndex is never used\n\ngo/src/fmt/scan.go\n(465, 5)  ST1012  error var complexError should have name of the form errFoo\n(466, 5)  ST1012  error var boolError should have name of the form errFoo\n\n✖ 6 problems (6 errors, 0 warnings)\n```\n\n## JSON {#json}\n\nThe JSON formatter emits one JSON object per problem found –\nthat is, it is a stream of objects, not an array.\nMost fields should be self-explanatory.\n\nThe `severity` field may be one of\n`\"error\"`, `\"warning\"` or `\"ignored\"`.\nWhether a problem is an error or a warning is determined by the `-fail` flag.\nThe value `\"ignored\"` is used for problems that were ignored,\nif the `-show-ignored` flag was provided.\n\n### Example output\n\nNote that actual output is not formatted nicely.\nThe example has been formatted to improve readability.\n\n```json\n{\n  \"code\": \"SA4006\",\n  \"severity\": \"error\",\n  \"location\": {\n    \"file\": \"/usr/lib/go/src/fmt/print.go\",\n    \"line\": 1082,\n    \"column\": 15\n  },\n  \"end\": {\n    \"file\": \"/usr/lib/go/src/fmt/print.go\",\n    \"line\": 1082,\n    \"column\": 25\n  },\n  \"message\": \"this value of afterIndex is never used\"\n}\n```\n"
  },
  {
    "path": "website/content/docs/running-staticcheck/editors.md",
    "content": "---\ntitle: Editors\ndescription: How to run Staticcheck in editors\ndraft: true\n---\n"
  },
  {
    "path": "website/content/sponsors.md",
    "content": "---\ntitle: \"Supporting Staticcheck's open source development\"\nlinkTitle: \"Sponsors\"\nmenu:\n  main:\n    weight: 4\n    pre: <i class='fas fa-heart'></i>\n---\n\n{{< blocks/section type=\"section\" color=\"white\"  >}}\n\n# Supporting Staticcheck's open source development\n\nStaticcheck is an open source project that is provided free of charge and without any expectations.\nNevertheless, working on it requires a considerable time investment.\nSome very generous people choose to support its development\nby donating money on [GitHub Sponsors](https://github.com/users/dominikh/sponsorship) or [Patreon](https://www.patreon.com/dominikh).\nWhile this is in no way expected of them, it is tremendously appreciated.\n\nIn addition to these individuals, a number of companies also\ndecide to support Staticcheck through monetary means.\nTheir contributions to open source ensure the viability and future development of Staticcheck.\nTheir support, too, is greatly appreciated.\n\n{{< sponsors.inline >}}\n{{ with $sponsors :=  $.Site.Data.sponsors.sponsors }}\nThe companies supporting Staticcheck are, in alphabetical order:\n\n<ul>\n  {{ range $sponsor := sort $sponsors \"name\" \"asc\" }}\n  {{ if $sponsor.enabled }}\n  <li><a href=\"{{ $sponsor.url }}\">{{ $sponsor.name }}</a></li>\n  {{ end }}\n  {{ end }}\n</ul>\n{{ end }}\n{{< /sponsors.inline >}}\n\nIf your company would like to support Staticcheck, please check out [GitHub Sponsors](https://github.com/users/dominikh/sponsorship)\nor [get in touch with me directly.](mailto:dominik@honnef.co)\nFor [$250 USD a month](https://github.com/users/dominikh/sponsorship?utf8=%E2%9C%93&tier_id=MDIyOk1hcmtldHBsYWNlTGlzdGluZ1BsYW4yNTAy&editing=false),\nwe will proudly display your logo on the project's homepage,\nshowing the world that you truly care about code quality and open source.\n\nFinally, every single user, individual and company alike, is to be thanked for using Staticcheck, providing feedback, requesting features and in general caring about code quality.\nWithout its users, there would be no Staticcheck.\n\n{{< /blocks/section >}}\n"
  },
  {
    "path": "website/go.mod",
    "content": "module honnef.co/go/tools/website\n\ngo 1.25.0\n\nreplace honnef.co/go/tools => ../\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c\n\thonnef.co/go/tools v0.0.0-00010101000000-000000000000\n)\n\nrequire (\n\tgolang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect\n\tgolang.org/x/mod v0.31.0 // indirect\n\tgolang.org/x/sync v0.19.0 // indirect\n\tgolang.org/x/tools v0.40.1-0.20260108161641-ca281cf95054 // indirect\n)\n"
  },
  {
    "path": "website/go.sum",
    "content": "github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=\ngithub.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngolang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=\ngolang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=\ngolang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=\ngolang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/tools v0.40.1-0.20260108161641-ca281cf95054 h1:CHVDrNHx9ZoOrNN9kKWYIbT5Rj+WF2rlwPkhbQQ5V4U=\ngolang.org/x/tools v0.40.1-0.20260108161641-ca281cf95054/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=\ngolang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=\ngolang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=\n"
  },
  {
    "path": "website/layouts/_internal/twitter_cards.html",
    "content": "<!--\n  Copied from Hugo, but modified to not use summary_large_image\n  cards for site-wide images. This is our logo, and Twitter says not to\n  use summary_large_image for logos\n -->\n\n{{- with $.Params.images -}}\n  <meta name=\"twitter:card\" content=\"summary_large_image\"/>\n  <meta name=\"twitter:image\" content=\"{{ index . 0 | absURL }}\"/>\n{{ else -}}\n  {{- $images := $.Resources.ByType \"image\" -}}\n  {{- $featured := $images.GetMatch \"*feature*\" -}}\n  {{- if not $featured }}{{ $featured = $images.GetMatch \"{*cover*,*thumbnail*}\" }}{{ end -}}\n  {{- with $featured -}}\n\t<meta name=\"twitter:card\" content=\"summary_large_image\"/>\n\t<meta name=\"twitter:image\" content=\"{{ $featured.Permalink }}\"/>\n  {{- else -}}\n\t<meta name=\"twitter:card\" content=\"summary\"/>\n\t{{- with $.Site.Params.images -}}\n\t  <meta name=\"twitter:image\" content=\"{{ index . 0 | absURL }}\"/>\n\t{{- end -}}\n  {{- end -}}\n{{- end }}\n<meta name=\"twitter:title\" content=\"{{ .Title }}\"/>\n<meta name=\"twitter:description\" content=\"{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}\"/>\n{{ with .Site.Social.twitter -}}\n  <meta name=\"twitter:site\" content=\"@{{ . }}\"/>\n{{ end -}}\n"
  },
  {
    "path": "website/layouts/changes/list.rss.xml",
    "content": "<!-- Copy of Docsy's layout, but\n\t - without a special case for first-level lists that would only include regular pages\n\t - using the actual section title, not title-casing the section name\n\t - not including a featured image\n\t - not including a zero pubDate\n\t - not incorrectly using safeHTML in dates\n\t - formatting the template in a readable way; we don't care about some empty lines in the XML\n-->\n\n<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n  <channel>\n    <title>{{ .Site.Title }} – {{ .Title }}</title>\n    <link>{{ .Permalink }}</link>\n    <description>Recent content {{ if ne  .Title  .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}</description>\n    <generator>Hugo -- gohugo.io</generator>\n\t{{ with .Site.LanguageCode }}\n      <language>{{.}}</language>\n\t{{end}}\n\t{{ with .Site.Author.email }}\n      <managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>\n\t{{end}}\n\t{{ with .Site.Author.email }}\n      <webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>\n\t{{end}}\n\t{{ with .Site.Copyright }}\n      <copyright>{{.}}</copyright>\n\t{{end}}\n\t{{ if not .Date.IsZero }}\n      <lastBuildDate>{{ .Date.Format \"Mon, 02 Jan 2006 15:04:05 -0700\" | safeHTML }}</lastBuildDate>\n\t{{ end }}\n    {{ with .OutputFormats.Get \"RSS\" }}\n      {{ printf \"<atom:link href=%q rel=\\\"self\\\" type=%q />\" .Permalink .MediaType | safeHTML }}\n    {{ end }}\n\n    {{ if not $.Section }}\n      {{ $sections := .Site.Params.rss_sections | default (slice \"blog\") }}\n      {{ .Scratch.Set \"rss_pages\" (first 50 (where $.Site.RegularPages \"Type\" \"in\" $sections )) }}\n    {{ else }}\n      {{ .Scratch.Set \"rss_pages\" (first 50 $.Pages) }}\n    {{ end }}\n    {{ range (.Scratch.Get \"rss_pages\")  }}\n    <item>\n      <title>{{ $.Page.Title }}: {{ .Title }}</title>\n      <link>{{ .Permalink }}</link>\n\t  {{ if not .Date.IsZero }}\n\t    <pubDate>{{ .Date.Format \"Mon, 02 Jan 2006 15:04:05 -0700\" }}</pubDate>\n\t  {{ end }}\n      {{ with .Site.Author.email }}\n\t    <author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>\n\t  {{end}}\n      <guid>{{ .Permalink }}</guid>\n      <description>\n\t\t{{ .Content | html }}\n      </description>\n    </item>\n    {{ end }}\n  </channel>\n</rss>\n"
  },
  {
    "path": "website/layouts/index.redir",
    "content": "{{ range $p := .Site.Pages }}\n{{- range .Aliases -}}\n{{ . }} {{ $p.RelPermalink }}\n{{ end }}\n{{- end -}}\n/pricing /sponsors 301\n/docs/staticcheck -> /docs/checks 301\n/docs/gosimple /docs/checks 301\n/issues/* https://github.com/dominikh/go-tools/issues/:splat 301\nhttp://staticcheck.io/* https://staticcheck.dev/:splat 301!\nhttps://staticcheck.io/* https://staticcheck.dev/:splat 301!\nhttp://www.staticcheck.io/* https://staticcheck.dev/:splat 301!\nhttps://www.staticcheck.io/* https://staticcheck.dev/:splat 301!\n"
  },
  {
    "path": "website/layouts/partials/breadcrumb.html",
    "content": "<!-- Copied from Docsy and modified to emit schema.org metadata -->\n\n{{ $isSingle := true -}}\n{{ with .Parent -}}\n  {{ $isSingle = .IsHome -}}\n{{ end -}}\n\n\n{{ .Scratch.Set \"breadcrumbMaxDepth\" 0 }}\n{{ template \"breadcrumbcount\" (dict \"p1\" . \"scratch\" .Scratch) }}\n{{ define \"breadcrumbcount\" }}\n  {{ .scratch.Set \"breadcrumbMaxDepth\" (add 1 (.scratch.Get \"breadcrumbMaxDepth\")) }}\n  {{ if .p1.Parent }}\n    {{ if not .p1.Parent.IsHome }}\n      {{ template \"breadcrumbcount\" (dict \"p1\" .p1.Parent \"scratch\" .scratch) }}\n    {{ end }}\n  {{ else if not .IsHome }}\n      {{ template \"breadcrumbcount\" (dict \"p1\" .p1.Site.Home \"scratch\" .scratch) }}\n  {{ end }}\n{{ end }}\n\n\n<nav aria-label=\"breadcrumb\" class=\"td-breadcrumbs\n    {{- if $isSingle }} td-breadcrumbs__single {{- end }}\">\n  <ol class=\"breadcrumb\" itemscope itemtype=\"https://schema.org/BreadcrumbList\">\n    {{- template \"breadcrumbnav\" (dict \"p1\" . \"p2\" . \"depth\" 0 \"scratch\" .Scratch) }}\n  </ol>\n</nav>\n\n{{- define \"breadcrumbnav\" -}}\n  {{ if .p1.Parent -}}\n    {{ if not .p1.Parent.IsHome -}}\n      {{ template \"breadcrumbnav\" (dict \"p1\" .p1.Parent \"p2\" .p2 \"depth\" (add .depth 1) \"scratch\" .scratch)  -}}\n    {{ end -}}\n  {{ else if not .p1.IsHome -}}\n    {{ template \"breadcrumbnav\" (dict \"p1\" .p1.Site.Home \"p2\" .p2 \"depth\" (add .depth 1) \"scratch\" .scratch)  -}}\n  {{ end -}}\n  {{ $isActive :=  eq .p1 .p2 }}\n  <li itemprop=\"itemListElement\" itemscope itemtype=\"https://schema.org/ListItem\" class=\"breadcrumb-item{{ if $isActive }} active{{ end }}\"\n      {{- if $isActive }} aria-current=\"page\"{{ end }}>\n    <a itemprop=\"item\" href=\"{{ .p1.Permalink }}\"\n      {{- if $isActive }} aria-disabled=\"true\" class=\"btn-link disabled\"{{ end -}}\n    >\n      <span itemprop=\"name\">{{- .p1.LinkTitle -}}</span>\n    </a>\n\t<meta itemprop=\"position\" content=\"{{ sub (.scratch.Get \"breadcrumbMaxDepth\") .depth }} \" />\n  </li>\n{{- end -}}\n"
  },
  {
    "path": "website/layouts/partials/footer.html",
    "content": "<!-- This is a copy of Docsy's footer, with the following changes: -->\n<!-- - added a sponsor bar on top of the footer -->\n<!-- - removed target=_blank from external links -->\n<!-- - add support for multiple copyright notices -->\n<!-- - align footer text on the left -->\n<!-- - remove icons/links from footer -->\n\n\n{{ define \"partials/sponsor-logo-resize\" }}\n{{- if gt .img.Width .width -}}\n{{- $rimg := .img.Resize (printf \"%dx\" .width) -}}\n{{- $rimg.RelPermalink }} {{ $rimg.Width -}}w,\n{{- end -}}\n{{ end }}\n\n{{ define \"partials/sponsor-logo\" }}\n{{ $img := resources.Get .logo }}\n\n<div class=\"sponsor\">\n  <a href=\"{{ .url }}\" >\n\t<!-- We'd prefer using $img.ResourceType, but that seems to be \"image\" even when it's a \"genericResource\" -->\n\t<img\n\t  alt=\"Logo of {{ .name }}\"\n\t  title=\"{{ .name }}\"\n\t  importance=\"low\"\n\t  src=\"{{ $img.RelPermalink }}\"\n\t  {{ if eq $img.MediaType.String \"image/webp\" }}\n\t  srcset=\"{{ $img.RelPermalink }} {{ $img.Width }}w,\n{{ partial \"partials/sponsor-logo-resize\" (dict \"img\" $img \"width\" 1200) }}\n{{ partial \"partials/sponsor-logo-resize\" (dict \"img\" $img \"width\" 800)  }}\n{{ partial \"partials/sponsor-logo-resize\" (dict \"img\" $img \"width\" 400)  }}\n{{ partial \"partials/sponsor-logo-resize\" (dict \"img\" $img \"width\" 200)  }}\n{{ partial \"partials/sponsor-logo-resize\" (dict \"img\" $img \"width\" 100)  }}\n{{ partial \"partials/sponsor-logo-resize\" (dict \"img\" $img \"width\" 50)   }}\"\n\t  sizes=\"160px\"\n\t  {{ else }}\n\t  data-foo=\"{{$img.MediaType}}\"\n\t  {{ end }}\n\t  >\n  </a>\n</div>\n{{ end }}\n\n\n{{ with $sponsors := $.Site.Data.sponsors.sponsors }}\n<section id=\"sponsors-bar\"  class=\"row td-box td-box--gradient td-box--height-auto d-print-none\">\n  <h1 class=\"text-center col-12\"><a href=\"/sponsors\">Sponsors</a></h1>\n  <div class=\"row section\" style=\"margin-left: auto; margin-right: auto;\">\n\t<div class=\"row align-items-center justify-content-center h-100\">\n\t  {{ range $sponsor := sort $sponsors \"name\" \"asc\" }}\n\t  {{ if $sponsor.enabled }}\n\t  {{ partial \"sponsor-logo\" (dict \"name\" $sponsor.name \"url\" $sponsor.url \"logo\" $sponsor.logo) }}\n\t  {{ end }}\n\t  {{ end }}\n\t</div>\n  </div>\n</section>\n{{ end }}\n\n{{ $links := .Site.Params.links }}\n<footer class=\"bg-dark py-5 row d-print-none\">\n  <div class=\"container-fluid mx-sm-5\">\n\t<div class=\"row\">\n\t  <div class=\"col-12\">\n\t\t{{ range $cp := .Site.Data.copyrights.copyrights }}\n\t\t<small class=\"text-white\">{{ $cp | safeHTML }}</small><br>\n\t\t{{ end }}\n\t\t{{ with .Site.Params.privacy_policy }}<small class=\"ml-1\"><a href=\"{{ . }}\">{{ T \"footer_privacy_policy\" }}</a></small>{{ end }}\n\t\t{{ if not .Site.Params.ui.footer_about_disable }}\n\t\t{{ with .Site.GetPage \"about\" }}<p class=\"mt-2\"><a href=\"{{ .RelPermalink }}\">{{ .Title }}</a></p>{{ end }}\n\t\t{{ end }}\n\t  </div>\n\t</div>\n  </div>\n</footer>\n{{ define \"footer-links-block\" }}\n<ul class=\"list-inline mb-0\">\n  {{ range . }}\n  <li class=\"list-inline-item mx-2 h3\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"{{ .name }}\" aria-label=\"{{ .name }}\">\n\t<a class=\"text-white\" href=\"{{ .url }}\" aria-label=\"{{ .name }}\">\n\t  <i class=\"{{ .icon }}\"></i>\n\t</a>\n  </li>\n  {{ end }}\n</ul>\n{{ end }}\n"
  },
  {
    "path": "website/layouts/partials/hooks/head-end.html",
    "content": "<link rel=\"preload\" href=\"https://fonts.gstatic.com/s/opensans/v26/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-mu0SC55I.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n<link rel=\"preload\" href=\"https://fonts.gstatic.com/s/opensans/v26/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n<link rel=\"preload\" href=\"/webfonts/fa-solid-900.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n<link rel=\"preload\" href=\"/webfonts/fa-brands-400.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n<link rel=\"preload\" href=\"https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,700,700i&display=swap\" as=\"style\" crossorigin>\n"
  },
  {
    "path": "website/layouts/partials/navbar.html",
    "content": "<!-- this is a copy of Docsy's navbar, with the difference that it doesn't use target=_blank for external links -->\n{{ $cover := and (.HasShortcode \"blocks/cover\") (not .Site.Params.ui.navbar_translucent_over_cover_disable) }}\n<nav class=\"js-navbar-scroll navbar navbar-expand navbar-dark {{ if $cover}} td-navbar-cover {{ end }}flex-column flex-md-row td-navbar\">\n        <a class=\"navbar-brand\" href=\"{{ .Site.Home.RelPermalink }}\">\n\t\t<span class=\"navbar-logo\">{{ if .Site.Params.ui.navbar_logo }}{{ with resources.Get \"icons/logo.svg\" }}{{ ( . | minify).Content | safeHTML }}{{ end }}{{ end }}</span><span class=\"text-uppercase font-weight-bold\">{{ .Site.Title }}</span>\n\t</a>\n\t<div class=\"td-navbar-nav-scroll ml-md-auto\" id=\"main_navbar\">\n\t\t<ul class=\"navbar-nav mt-2 mt-lg-0\">\n\t\t\t{{ $p := . }}\n\t\t\t{{ range .Site.Menus.main }}\n\t\t\t<li class=\"nav-item mr-4 mb-2 mb-lg-0\">\n\t\t\t\t{{ $active := or ($p.IsMenuCurrent \"main\" .) ($p.HasMenuCurrent \"main\" .) }}\n\t\t\t\t{{ with .Page }}\n\t\t\t\t{{ $active = or $active ( $.IsDescendant .)  }}\n\t\t\t\t{{ end }}\n\t\t\t\t{{ $pre := .Pre }}\n\t\t\t\t{{ $post := .Post }}\n\t\t\t\t{{ $url := urls.Parse .URL }}\n\t\t\t\t{{ $baseurl := urls.Parse $.Site.Params.Baseurl }}\n\t\t\t\t<a class=\"nav-link{{if $active }} active{{end}}\" href=\"{{ with .Page }}{{ .RelPermalink }}{{ else }}{{ .URL | relLangURL }}{{ end }}\">{{ with .Pre}}{{ $pre }}{{ end }}<span{{if $active }} class=\"active\"{{end}}>{{ .Name }}</span>{{ with .Post}}{{ $post }}{{ end }}</a>\n\t\t\t</li>\n\t\t\t{{ end }}\n\t\t\t{{ if  .Site.Params.versions }}\n\t\t\t<li class=\"nav-item dropdown mr-4 d-none d-lg-block\">\n\t\t\t\t{{ partial \"navbar-version-selector.html\" . }}\n\t\t\t</li>\n\t\t\t{{ end }}\n\t\t\t{{ if  (gt (len .Site.Home.Translations) 0) }}\n\t\t\t<li class=\"nav-item dropdown mr-4 d-none d-lg-block\">\n\t\t\t\t{{ partial \"navbar-lang-selector.html\" . }}\n\t\t\t</li>\n\t\t\t{{ end }}\n\t\t</ul>\n\t</div>\n\t<div class=\"navbar-nav d-none d-lg-block\">{{ partial \"search-input.html\" . }}</div>\n</nav>\n"
  },
  {
    "path": "website/layouts/shortcodes/check.html",
    "content": "{{ $name := .Get 0 -}}\n{{ $check := index $.Site.Data.checks.Checks $name -}}\n{{ with .Get 0 }}<a title=\"{{ $check.Title }}\" href=\"{{ ref $.Page \"/docs/checks\" }}#{{$name}}\">{{$name}}</a>{{ end -}}\n"
  },
  {
    "path": "website/layouts/shortcodes/commit.html",
    "content": "{{ with .Get 0 }}<a title=\"commit {{ . }}\" href=\"https://github.com/dominikh/go-tools/commit/{{ . }}\">{{ . }}</a>{{ end -}}\n"
  },
  {
    "path": "website/layouts/shortcodes/content.html",
    "content": "{{$file := .Get 0}}\n{{ with .Site.GetPage $file }}{{ .Content | markdownify }}{{ end }}\n"
  },
  {
    "path": "website/layouts/shortcodes/details.html",
    "content": "<details>\n  <summary>{{ .Get 0 }}</summary>\n  {{ .Inner | markdownify }}\n</details>\n"
  },
  {
    "path": "website/layouts/shortcodes/faq/list.html",
    "content": "<div itemscope itemtype=\"https://schema.org/FAQPage\">\n  {{ .Inner }}\n</div>\n"
  },
  {
    "path": "website/layouts/shortcodes/faq/question.md",
    "content": "<div itemscope itemprop=\"mainEntity\" itemtype=\"https://schema.org/Question\">\n\n## <span itemprop=\"name\">{{ .Get \"question\" }}</span> {#{{ .Get \"id\" }}}\n\n<div itemscope itemprop=\"acceptedAnswer\" itemtype=\"https://schema.org/Answer\">\n<div itemprop=\"text\">\n{{ .Inner }}\n</div>\n</div>\n</div>\n"
  },
  {
    "path": "website/layouts/shortcodes/issue.html",
    "content": "{{ with .Get 0 }}<a title=\"issue {{ . }}\" href=\"https://staticcheck.dev/issues/{{ . }}\">issue {{ . }}</a>{{ end -}}\n"
  },
  {
    "path": "website/layouts/shortcodes/issueref.html",
    "content": "https://staticcheck.dev/issues/{{ .Get 0 }}\n"
  },
  {
    "path": "website/layouts/shortcodes/option.html",
    "content": "<a href=\"{{ ref . \"/docs/configuration/options\" }}#{{ .Get 0 }}\">{{ .Get 0 }}</a>{{\"\" -}}\n"
  },
  {
    "path": "website/package.json",
    "content": "{\n  \"dependencies\": {\n    \"autoprefixer\": \"^10.4.13\",\n    \"postcss\": \"^8.4.31\",\n    \"postcss-cli\": \"^10.1.0\"\n  }\n}\n"
  },
  {
    "path": "website/shell.nix",
    "content": "{ pkgs ? import <nixpkgs> {} }:\npkgs.mkShell {\n  # nativeBuildInputs is usually what you want -- tools you need to run\n  nativeBuildInputs = with pkgs; [ postcss-cli nodejs ];\n}\n"
  },
  {
    "path": "website/static/_headers",
    "content": "/img/*\n  cache-control: public\n  cache-control: max-age=604800\n  cache-control: immutable\n\n/webfonts/*\n  cache-control: public\n  cache-control: max-age=604800\n  cache-control: immutable\n\n/scss/*.css\n  cache-control: public\n  cache-control: max-age=604800\n  cache-control: immutable\n\n/js/*.js\n  cache-control: public\n  cache-control: max-age=604800\n  cache-control: immutable\n"
  },
  {
    "path": "website/website.go",
    "content": "package website\n"
  }
]