{"id":20820365,"url":"https://github.com/kyoh86/exportloopref","last_synced_at":"2025-05-16T09:05:31.898Z","repository":{"id":37396068,"uuid":"256768552","full_name":"kyoh86/exportloopref","owner":"kyoh86","description":null,"archived":false,"fork":false,"pushed_at":"2024-06-16T13:46:43.000Z","size":66,"stargazers_count":120,"open_issues_count":2,"forks_count":5,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-09T04:03:19.864Z","etag":null,"topics":["diagnostics","go","golang","linter"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kyoh86.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-18T14:05:13.000Z","updated_at":"2025-03-04T10:10:39.000Z","dependencies_parsed_at":"2024-06-18T14:08:05.485Z","dependency_job_id":null,"html_url":"https://github.com/kyoh86/exportloopref","commit_stats":{"total_commits":40,"total_committers":5,"mean_commits":8.0,"dds":0.09999999999999998,"last_synced_commit":"5bb2638a074d97c14fb8a515b06d64c5bde024ab"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyoh86%2Fexportloopref","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyoh86%2Fexportloopref/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyoh86%2Fexportloopref/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kyoh86%2Fexportloopref/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kyoh86","download_url":"https://codeload.github.com/kyoh86/exportloopref/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254501557,"owners_count":22081528,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["diagnostics","go","golang","linter"],"created_at":"2024-11-17T22:09:02.066Z","updated_at":"2025-05-16T09:05:26.885Z","avatar_url":"https://github.com/kyoh86.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# exportloopref\n\nAn analyzer that finds exporting pointers for loop variables.\n![](https://repository-images.githubusercontent.com/256768552/a1c5bb80-dd73-11eb-9453-e520f517e730)\nPin them all!\n\n**As of Go 1.22, this problem no longer occurs and fixed by Go team, see [here](https://go.dev/blog/loopvar-preview)**\n\n[![PkgGoDev](https://pkg.go.dev/badge/kyoh86/exportloopref)](https://pkg.go.dev/kyoh86/exportloopref)\n[![Go Report Card](https://goreportcard.com/badge/github.com/kyoh86/exportloopref)](https://goreportcard.com/report/github.com/kyoh86/exportloopref)\n[![Coverage Status](https://img.shields.io/codecov/c/github/kyoh86/exportloopref.svg)](https://codecov.io/gh/kyoh86/exportloopref)\n[![Release](https://github.com/kyoh86/exportloopref/workflows/Release/badge.svg)](https://github.com/kyoh86/exportloopref/releases)\n\n## What's this?\n\nSample problem code from: https://github.com/kyoh86/exportloopref/blob/main/testdata/src/simple/simple.go\n\n```go\npackage main\n\nfunc main() {\n\tvar intArray [4]*int\n\tvar intSlice []*int\n\tvar intRef *int\n\tvar intStr struct{ x *int }\n\n\tprintln(\"loop expecting 10, 11, 12, 13\")\n\tfor i, p := range []int{10, 11, 12, 13} {\n\t\tprintp(\u0026p)                      // not a diagnostic\n\t\tintSlice = append(intSlice, \u0026p) // want \"exporting a pointer for the loop variable p\"\n\t\tintArray[i] = \u0026p                // want \"exporting a pointer for the loop variable p\"\n\t\tif i%2 == 0 {\n\t\t\tintRef = \u0026p   // want \"exporting a pointer for the loop variable p\"\n\t\t\tintStr.x = \u0026p // want \"exporting a pointer for the loop variable p\"\n\t\t}\n\t\tvar vStr struct{ x *int }\n\t\tvar vArray [4]*int\n\t\tvar v *int\n\t\tif i%2 == 0 {\n\t\t\tv = \u0026p         // not a diagnostic (x is local variable)\n\t\t\tvArray[1] = \u0026p // not a diagnostic (x is local variable)\n\t\t\tvStr.x = \u0026p\n\t\t}\n\t\t_ = v\n\t}\n\n\tprintln(`slice expecting \"10, 11, 12, 13\" but \"13, 13, 13, 13\"`)\n\tfor _, p := range intSlice {\n\t\tprintp(p)\n\t}\n\tprintln(`array expecting \"10, 11, 12, 13\" but \"13, 13, 13, 13\"`)\n\tfor _, p := range intArray {\n\t\tprintp(p)\n\t}\n\tprintln(`captured value expecting \"12\" but \"13\"`)\n\tprintp(intRef)\n}\n\nfunc printp(p *int) {\n\tprintln(*p)\n}\n```\n\nIn Go, the `p` variable in the above loops is actually a single variable.\nSo in many case (like the above), using it makes for us annoying bugs.\n\nYou can find them with `exportloopref`, and fix it.\n\n```go\npackage main\n\nfunc main() {\n\tvar intArray [4]*int\n\tvar intSlice []*int\n\tvar intRef *int\n\tvar intStr struct{ x *int }\n\n\tprintln(\"loop expecting 10, 11, 12, 13\")\n\tfor i, p := range []int{10, 11, 12, 13} {\n    p := p                          // FIX variable into the local variable\n\t\tprintp(\u0026p)\n\t\tintSlice = append(intSlice, \u0026p) \n\t\tintArray[i] = \u0026p\n\t\tif i%2 == 0 {\n\t\t\tintRef = \u0026p\n\t\t\tintStr.x = \u0026p\n\t\t}\n\t\tvar vStr struct{ x *int }\n\t\tvar vArray [4]*int\n\t\tvar v *int\n\t\tif i%2 == 0 {\n\t\t\tv = \u0026p\n\t\t\tvArray[1] = \u0026p\n\t\t\tvStr.x = \u0026p\n\t\t}\n\t\t_ = v\n\t}\n\n\tprintln(`slice expecting \"10, 11, 12, 13\"`)\n\tfor _, p := range intSlice {\n\t\tprintp(p)\n\t}\n\tprintln(`array expecting \"10, 11, 12, 13\"`)\n\tfor _, p := range intArray {\n\t\tprintp(p)\n\t}\n\tprintln(`captured value expecting \"12\"`)\n\tprintp(intRef)\n}\n\nfunc printp(p *int) {\n\tprintln(*p)\n}\n```\n\nref: https://github.com/kyoh86/exportloopref/blob/main/testdata/src/fixed/fixed.go\n\n## Sensing policy\n\nI want to make exportloopref as accurately as possible.\nSo some cases of lints will be false-negative.\n\ne.g.\n\n```go\nvar s Foo\nfor _, p := range []int{10, 11, 12, 13} {\n  s.Bar(\u0026p) // If s stores the pointer, it will be bug.\n}\n```\n\nIf you want to report all of lints (with some false-positives),\nyou should use [looppointer](https://github.com/kyoh86/looppointer).\n\n### Known false negatives\n\nCase 1: pass the pointer to function to export.\n\nCase 2: pass the pointer to local variable, and export it.\n\n```go\npackage main\n\ntype List []*int\n\nfunc (l *List) AppendP(p *int) {\n\t*l = append(*l, p)\n}\n\nfunc main() {\n\tvar slice []*int\n\tlist := List{}\n\n\tprintln(\"loop expect exporting 10, 11, 12, 13\")\n\tfor _, v := range []int{10, 11, 12, 13} {\n\t\tlist.AppendP(\u0026v) // Case 1: wanted \"exporting a pointer for the loop variable v\", but cannot be found\n\n\t\tp := \u0026v                  // p is the local variable\n\t\tslice = append(slice, p) // Case 2: wanted \"exporting a pointer for the loop variable v\", but cannot be found\n\t}\n\n\tprintln(`slice expecting \"10, 11, 12, 13\" but \"13, 13, 13, 13\"`)\n\tfor _, p := range slice {\n\t\tprintp(p)\n\t}\n\tprintln(`array expecting \"10, 11, 12, 13\" but \"13, 13, 13, 13\"`)\n\tfor _, p := range ([]*int)(list) {\n\t\tprintp(p)\n\t}\n}\n\nfunc printp(p *int) {\n\tprintln(*p)\n}\n```\n\n## Install\n\ngo:\n\n```console\n$ go install github.com/kyoh86/exportloopref/cmd/exportloopref@latest\n```\n\n[homebrew](https://brew.sh/):\n\n```console\n$ brew install kyoh86/tap/exportloopref\n```\n\n[gordon](https://github.com/kyoh86/gordon):\n\n```console\n$ gordon install kyoh86/exportloopref\n```\n\n## Usage\n\n```\nexportloopref [-flag] [package]\n```\n\n### Flags\n\n| Flag | Description |\n| --- | --- |\n| -V                 | print version and exit |\n| -all               | no effect (deprecated) |\n| -c int             | display offending line with this many lines of context (default -1) |\n| -cpuprofile string | write CPU profile to this file |\n| -debug string      | debug flags, any subset of \"fpstv\" |\n| -fix               | apply all suggested fixes |\n| -flags             | print analyzer flags in JSON |\n| -json              | emit JSON output |\n| -memprofile string | write memory profile to this file |\n| -source            | no effect (deprecated) |\n| -tags string       | no effect (deprecated) |\n| -trace string      | write trace log to this file |\n| -v                 | no effect (deprecated) |\n\n# LICENSE\n\n[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg)](http://www.opensource.org/licenses/MIT)\n\nThis is distributed under the [MIT License](http://www.opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyoh86%2Fexportloopref","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkyoh86%2Fexportloopref","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyoh86%2Fexportloopref/lists"}