{"id":44707532,"url":"https://github.com/nick-jones/astpath","last_synced_at":"2026-02-15T11:10:58.187Z","repository":{"id":57558383,"uuid":"271867494","full_name":"nick-jones/astpath","owner":"nick-jones","description":"XPath query over Go ASTs ","archived":false,"fork":false,"pushed_at":"2020-06-17T18:25:42.000Z","size":28,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-06-20T05:15:12.305Z","etag":null,"topics":["ast","go-ast","golang","golang-ast","xpath","xpath-query"],"latest_commit_sha":null,"homepage":"","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/nick-jones.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}},"created_at":"2020-06-12T18:42:53.000Z","updated_at":"2024-06-20T05:15:12.307Z","dependencies_parsed_at":"2022-08-28T09:32:26.965Z","dependency_job_id":null,"html_url":"https://github.com/nick-jones/astpath","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nick-jones/astpath","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nick-jones%2Fastpath","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nick-jones%2Fastpath/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nick-jones%2Fastpath/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nick-jones%2Fastpath/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nick-jones","download_url":"https://codeload.github.com/nick-jones/astpath/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nick-jones%2Fastpath/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29476300,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T10:25:47.032Z","status":"ssl_error","status_checked_at":"2026-02-15T10:25:01.815Z","response_time":118,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["ast","go-ast","golang","golang-ast","xpath","xpath-query"],"created_at":"2026-02-15T11:10:57.526Z","updated_at":"2026-02-15T11:10:58.182Z","avatar_url":"https://github.com/nick-jones.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# astpath\n\nQuick hack that provides XPath querying over Go ASTs. Inspired by [hchasestevens/astpath](https://github.com/hchasestevens/astpath)\nfor Python.\n\n## Installing\n\n```\nGO111MODULE=on go get github.com/nick-jones/astpath\n```\n\n## Usage\n\n```\nastpath \u003cxpath-expr\u003e \u003cfile-path\u003e\n```\n\nThe file path can be a directory, in which case all files in that directly are checked recursively.\n\n## Examples\n\nTaking a basic and crude example:\n\n```go\npackage test\n\nimport (\n\t\"log\"\n\t\"strings\"\n)\n\nfunc repeatConditional(str string, count int, fn func(string) bool) string {\n\tif fn(str) {\n\t\tlog.Println(\"hit!\")\n\t\treturn strings.Repeat(str, count)\n\t}\n\tlog.Printf(\"str = %s\", str)\n\treturn str\n}\n```\n\nTo view the raw XML output for a single file:\n\n```\n$ astpath --template='{{.XML}}' '/File' test.go | xmllint --format - | xmllint --format -\n```\n\n```xml    \n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cFile pos-start=\"1\" pos-end=\"239\"\u003e\n  \u003cIdent name=\"test\" pos-start=\"9\" pos-end=\"13\"/\u003e\n  \u003cGenDecl pos-start=\"15\" pos-end=\"43\"\u003e\n    \u003cImportSpec pos-start=\"25\" pos-end=\"30\"\u003e\n      \u003cBasicLit kind=\"STRING\" value=\"log\" pos-start=\"25\" pos-end=\"30\"/\u003e\n    \u003c/ImportSpec\u003e\n    \u003cImportSpec pos-start=\"32\" pos-end=\"41\"\u003e\n      \u003cBasicLit kind=\"STRING\" value=\"strings\" pos-start=\"32\" pos-end=\"41\"/\u003e\n    \u003c/ImportSpec\u003e\n  \u003c/GenDecl\u003e\n  \u003cFuncDecl pos-start=\"45\" pos-end=\"239\"\u003e\n    \u003cIdent name=\"repeatConditional\" pos-start=\"50\" pos-end=\"67\"\u003e\n      \u003cObject name=\"repeatConditional\" kind=\"func\"/\u003e\n    \u003c/Ident\u003e\n    \u003cFuncType pos-start=\"45\" pos-end=\"119\"\u003e\n      \u003cFieldList pos-start=\"67\" pos-end=\"112\"\u003e\n        \u003cField pos-start=\"68\" pos-end=\"78\"\u003e\n          \u003cIdent name=\"str\" pos-start=\"68\" pos-end=\"71\"\u003e\n            \u003cObject name=\"str\" kind=\"var\"/\u003e\n          \u003c/Ident\u003e\n          \u003cIdent name=\"string\" pos-start=\"72\" pos-end=\"78\"/\u003e\n        \u003c/Field\u003e\n        \u003cField pos-start=\"80\" pos-end=\"89\"\u003e\n          \u003cIdent name=\"count\" pos-start=\"80\" pos-end=\"85\"\u003e\n            \u003cObject name=\"count\" kind=\"var\"/\u003e\n          \u003c/Ident\u003e\n          \u003cIdent name=\"int\" pos-start=\"86\" pos-end=\"89\"/\u003e\n        \u003c/Field\u003e\n        \u003cField pos-start=\"91\" pos-end=\"111\"\u003e\n          \u003cIdent name=\"fn\" pos-start=\"91\" pos-end=\"93\"\u003e\n            \u003cObject name=\"fn\" kind=\"var\"/\u003e\n          \u003c/Ident\u003e\n          \u003cFuncType pos-start=\"94\" pos-end=\"111\"\u003e\n            \u003cFieldList pos-start=\"98\" pos-end=\"106\"\u003e\n              \u003cField pos-start=\"99\" pos-end=\"105\"\u003e\n                \u003cIdent name=\"string\" pos-start=\"99\" pos-end=\"105\"/\u003e\n              \u003c/Field\u003e\n            \u003c/FieldList\u003e\n            \u003cFieldList pos-start=\"107\" pos-end=\"111\"\u003e\n              \u003cField pos-start=\"107\" pos-end=\"111\"\u003e\n                \u003cIdent name=\"bool\" pos-start=\"107\" pos-end=\"111\"/\u003e\n              \u003c/Field\u003e\n            \u003c/FieldList\u003e\n          \u003c/FuncType\u003e\n        \u003c/Field\u003e\n      \u003c/FieldList\u003e\n      \u003cFieldList pos-start=\"113\" pos-end=\"119\"\u003e\n        \u003cField pos-start=\"113\" pos-end=\"119\"\u003e\n          \u003cIdent name=\"string\" pos-start=\"113\" pos-end=\"119\"/\u003e\n        \u003c/Field\u003e\n      \u003c/FieldList\u003e\n    \u003c/FuncType\u003e\n    \u003cBlockStmt pos-start=\"120\" pos-end=\"239\"\u003e\n      \u003cIfStmt pos-start=\"123\" pos-end=\"196\"\u003e\n        \u003cCallExpr pos-start=\"126\" pos-end=\"133\"\u003e\n          \u003cIdent name=\"fn\" pos-start=\"126\" pos-end=\"128\"\u003e\n            \u003cObject name=\"fn\" kind=\"var\"/\u003e\n          \u003c/Ident\u003e\n          \u003cIdent name=\"str\" pos-start=\"129\" pos-end=\"132\"\u003e\n            \u003cObject name=\"str\" kind=\"var\"/\u003e\n          \u003c/Ident\u003e\n        \u003c/CallExpr\u003e\n        \u003cBlockStmt pos-start=\"134\" pos-end=\"196\"\u003e\n          \u003cExprStmt pos-start=\"138\" pos-end=\"157\"\u003e\n            \u003cCallExpr pos-start=\"138\" pos-end=\"157\"\u003e\n              \u003cSelectorExpr pos-start=\"138\" pos-end=\"149\"\u003e\n                \u003cIdent name=\"log\" pos-start=\"138\" pos-end=\"141\"/\u003e\n                \u003cIdent name=\"Println\" pos-start=\"142\" pos-end=\"149\"/\u003e\n              \u003c/SelectorExpr\u003e\n              \u003cBasicLit kind=\"STRING\" value=\"hit!\" pos-start=\"150\" pos-end=\"156\"/\u003e\n            \u003c/CallExpr\u003e\n          \u003c/ExprStmt\u003e\n          \u003cReturnStmt pos-start=\"160\" pos-end=\"193\"\u003e\n            \u003cCallExpr pos-start=\"167\" pos-end=\"193\"\u003e\n              \u003cSelectorExpr pos-start=\"167\" pos-end=\"181\"\u003e\n                \u003cIdent name=\"strings\" pos-start=\"167\" pos-end=\"174\"/\u003e\n                \u003cIdent name=\"Repeat\" pos-start=\"175\" pos-end=\"181\"/\u003e\n              \u003c/SelectorExpr\u003e\n              \u003cIdent name=\"str\" pos-start=\"182\" pos-end=\"185\"\u003e\n                \u003cObject name=\"str\" kind=\"var\"/\u003e\n              \u003c/Ident\u003e\n              \u003cIdent name=\"count\" pos-start=\"187\" pos-end=\"192\"\u003e\n                \u003cObject name=\"count\" kind=\"var\"/\u003e\n              \u003c/Ident\u003e\n            \u003c/CallExpr\u003e\n          \u003c/ReturnStmt\u003e\n        \u003c/BlockStmt\u003e\n      \u003c/IfStmt\u003e\n      \u003cExprStmt pos-start=\"198\" pos-end=\"225\"\u003e\n        \u003cCallExpr pos-start=\"198\" pos-end=\"225\"\u003e\n          \u003cSelectorExpr pos-start=\"198\" pos-end=\"208\"\u003e\n            \u003cIdent name=\"log\" pos-start=\"198\" pos-end=\"201\"/\u003e\n            \u003cIdent name=\"Printf\" pos-start=\"202\" pos-end=\"208\"/\u003e\n          \u003c/SelectorExpr\u003e\n          \u003cBasicLit kind=\"STRING\" value=\"str = %s\" pos-start=\"209\" pos-end=\"219\"/\u003e\n          \u003cIdent name=\"str\" pos-start=\"221\" pos-end=\"224\"\u003e\n            \u003cObject name=\"str\" kind=\"var\"/\u003e\n          \u003c/Ident\u003e\n        \u003c/CallExpr\u003e\n      \u003c/ExprStmt\u003e\n      \u003cReturnStmt pos-start=\"227\" pos-end=\"237\"\u003e\n        \u003cIdent name=\"str\" pos-start=\"234\" pos-end=\"237\"\u003e\n          \u003cObject name=\"str\" kind=\"var\"/\u003e\n        \u003c/Ident\u003e\n      \u003c/ReturnStmt\u003e\n    \u003c/BlockStmt\u003e\n  \u003c/FuncDecl\u003e\n\u003c/File\u003e\n```\n\nExtract import paths:\n\n```\n$ astpath '//ImportSpec' test.go\ntest.go:4:2 \u003e \"log\"\ntest.go:5:2 \u003e \"strings\"\n```\n\n... or\n\n```\n$ astpath --template='{{.XMLInner}}' '//ImportSpec/BasicLit/@value' test.go\nlog\nstrings\n```\n\nLocate `log.*` function calls:\n\n```\n$ astpath '//CallExpr/SelectorExpr[./Ident[1]/@name=\"log\"]' test.go\ntest.go:10:3 \u003e log.Println\ntest.go:13:2 \u003e log.Printf\n```\n\nLocate `log.Printf` function calls:\n\n```\n$ astpath '//CallExpr/SelectorExpr[./Ident[1]/@name=\"log\"][./Ident[2]/@name=\"Printf\"]' test.go\ntest.go:13:2 \u003e log.Printf\n```\n\n\n\n ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnick-jones%2Fastpath","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnick-jones%2Fastpath","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnick-jones%2Fastpath/lists"}