{"action":"created","comment":{"url":"https://api.github.com/repos/PieselBois/kfulint/pulls/comments/186968093","pull_request_review_id":118649961,"id":186968093,"diff_hunk":"@@ -24,63 +24,95 @@ func NewTypeGuardChecker(ctx *Context) *TypeGuardChecker {\n // Check runs type switch checks for f.\n func (c *TypeGuardChecker) Check(f *ast.File) []Warning {\n \tc.warnings = c.warnings[:0]\n-\t// TODO(quasilyte): do recursive analysis. Type switches\n-\t// may have other type switches inside their clauses that\n-\t// can trigger warnings as well.\n-\tfor _, decl := range collectFuncDecls(f) {\n-\t\tif decl.Body == nil {\n-\t\t\tcontinue\n-\t\t}\n-\t\tast.Inspect(decl.Body, func(x ast.Node) bool {\n-\t\t\tif stmt, ok := x.(*ast.TypeSwitchStmt); ok {\n-\t\t\t\tc.check(stmt)\n-\t\t\t\treturn false\n-\t\t\t}\n-\t\t\treturn true\n-\t\t})\n-\t}\n+\tinspectFuncBodies(f, c.inspectNode)\n \treturn c.warnings\n }\n \n-func (c *TypeGuardChecker) check(root *ast.TypeSwitchStmt) {\n-\tif _, ok := root.Assign.(*ast.ExprStmt); !ok {\n-\t\treturn\n+func (c *TypeGuardChecker) inspectNode(x ast.Node) bool {\n+\tif _, ok := x.(ast.Stmt); !ok {\n+\t\treturn false\n+\t}\n+\tif stmt, ok := x.(*ast.TypeSwitchStmt); ok {\n+\t\tc.checkTypeSwitch(stmt)\n+\t}\n+\treturn true\n+}\n+\n+func (c *TypeGuardChecker) checkTypeSwitch(root *ast.TypeSwitchStmt) {\n+\tif _, ok := root.Assign.(*ast.AssignStmt); ok {\n+\t\treturn // Already with type guard\n \t}\n-\tassert, ok := root.Assign.(*ast.ExprStmt).X.(*ast.TypeAssertExpr)\n-\tif !ok {\n-\t\treturn\n+\t// Must be a *ast.ExprStmt then.\n+\texpr := root.Assign.(*ast.ExprStmt).X.(*ast.TypeAssertExpr).X\n+\tobject := c.ctx.TypesInfo.ObjectOf(c.identOf(expr))\n+\tif object == nil {\n+\t\treturn // Give up: can't handle shadowing without object\n \t}\n \n \tfor i, clause := range root.Body.List {\n \t\tclause := clause.(*ast.CaseClause)\n \t\t// Multiple types in a list mean that assert.X will have\n-\t\t// a type of interface{} inside body.\n+\t\t// a type of interface{} inside clause body.\n \t\t// We are looking for precise type case.\n \t\tif len(clause.List) != 1 {\n \t\t\tcontinue\n \t\t}\n-\t\ttyp := clause.List[0]\n+\t\t// Create artifical node just for matching.\n+\t\tassert1 := ast.TypeAssertExpr{X: expr, Type: clause.List[0]}\n \t\tfor _, stmt := range clause.Body {\n-\t\t\tif c.findTypeAssert(stmt, assert.X, typ) {\n+\t\t\tassert2 := c.findEqual(stmt, &assert1)\n+\t\t\tif object == c.ctx.TypesInfo.ObjectOf(c.identOf(assert2)) {\n \t\t\t\tc.warn(root, i)\n \t\t\t\tbreak\n \t\t\t}\n \t\t}\n \t}\n }\n \n-func (c *TypeGuardChecker) findTypeAssert(root ast.Stmt, expr, typ ast.Expr) bool {\n-\tfound := false\n-\t// TODO(quasilyte): inspect does not end the traverse after false is returned.\n-\tast.Inspect(root, func(x ast.Node) bool {\n-\t\tif assert, ok := x.(*ast.TypeAssertExpr); ok {\n-\t\t\tfound = astcmp.EqualExpr(expr, assert.X) &&\n-\t\t\t\tastcmp.EqualExpr(typ, assert.Type)\n-\t\t\treturn !found\n+// findEqual walks tree root trying to find node that is equal to x.\n+// Matched node is returned.\n+func (c *TypeGuardChecker) findEqual(root, x ast.Node) (found ast.Node) {\n+\tdefer func() {\n+\t\tif r := recover(); r != nil {\n+\t\t\tpanic(r)","path":"lint/type-guard_checker.go","position":77,"original_position":86,"commit_id":"8aee9304af63cd2a2f873e36260616289e827b1e","original_commit_id":"8aee9304af63cd2a2f873e36260616289e827b1e","user":{"login":"Quasilyte","id":6286655,"avatar_url":"https://avatars3.githubusercontent.com/u/6286655?v=4","gravatar_id":"","url":"https://api.github.com/users/Quasilyte","html_url":"https://github.com/Quasilyte","followers_url":"https://api.github.com/users/Quasilyte/followers","following_url":"https://api.github.com/users/Quasilyte/following{/other_user}","gists_url":"https://api.github.com/users/Quasilyte/gists{/gist_id}","starred_url":"https://api.github.com/users/Quasilyte/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Quasilyte/subscriptions","organizations_url":"https://api.github.com/users/Quasilyte/orgs","repos_url":"https://api.github.com/users/Quasilyte/repos","events_url":"https://api.github.com/users/Quasilyte/events{/privacy}","received_events_url":"https://api.github.com/users/Quasilyte/received_events","type":"User","site_admin":false},"body":"There are multiple ways, not even counting making our own traverse functions:\r\n1. Take `golang.org/x/tools/go/ast/astutil` and use it's `Apply`. It's more powerful and has features we want. (Side note, look [how it handles traversal abort](https://github.com/golang/tools/blob/48418e5732e1b1e2a10207c8007a5f959e422f20/go/ast/astutil/rewrite.go#L45-L50)).\r\n2. Use `ast.Walk` with special-purpose visitors that keep `cancel` field inside them and set it to true when certain condition is met.\r\n\r\nI'm going to vendor astutil for now.","created_at":"2018-05-09T08:28:40Z","updated_at":"2018-05-09T08:28:40Z","html_url":"https://github.com/PieselBois/kfulint/pull/19#discussion_r186968093","pull_request_url":"https://api.github.com/repos/PieselBois/kfulint/pulls/19","author_association":"COLLABORATOR","_links":{"self":{"href":"https://api.github.com/repos/PieselBois/kfulint/pulls/comments/186968093"},"html":{"href":"https://github.com/PieselBois/kfulint/pull/19#discussion_r186968093"},"pull_request":{"href":"https://api.github.com/repos/PieselBois/kfulint/pulls/19"}},"in_reply_to_id":186863051},"pull_request":{"url":"https://api.github.com/repos/PieselBois/kfulint/pulls/19","id":186548830,"html_url":"https://github.com/PieselBois/kfulint/pull/19","diff_url":"https://github.com/PieselBois/kfulint/pull/19.diff","patch_url":"https://github.com/PieselBois/kfulint/pull/19.patch","issue_url":"https://api.github.com/repos/PieselBois/kfulint/issues/19","number":19,"state":"open","locked":false,"title":"Quasilyte/type guard v1","user":{"login":"Quasilyte","id":6286655,"avatar_url":"https://avatars3.githubusercontent.com/u/6286655?v=4","gravatar_id":"","url":"https://api.github.com/users/Quasilyte","html_url":"https://github.com/Quasilyte","followers_url":"https://api.github.com/users/Quasilyte/followers","following_url":"https://api.github.com/users/Quasilyte/following{/other_user}","gists_url":"https://api.github.com/users/Quasilyte/gists{/gist_id}","starred_url":"https://api.github.com/users/Quasilyte/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Quasilyte/subscriptions","organizations_url":"https://api.github.com/users/Quasilyte/orgs","repos_url":"https://api.github.com/users/Quasilyte/repos","events_url":"https://api.github.com/users/Quasilyte/events{/privacy}","received_events_url":"https://api.github.com/users/Quasilyte/received_events","type":"User","site_admin":false},"body":"Does trigger for cases from `testdata/type-guard`.\r\nDoes not handle nested type switches if containing type switch already triggered warning.\r\nWill fix that later (todo is on it's place).","created_at":"2018-05-08T07:29:18Z","updated_at":"2018-05-09T08:28:40Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9367be9143db41366d84cd576c010c066299d993","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/PieselBois/kfulint/pulls/19/commits","review_comments_url":"https://api.github.com/repos/PieselBois/kfulint/pulls/19/comments","review_comment_url":"https://api.github.com/repos/PieselBois/kfulint/pulls/comments{/number}","comments_url":"https://api.github.com/repos/PieselBois/kfulint/issues/19/comments","statuses_url":"https://api.github.com/repos/PieselBois/kfulint/statuses/8aee9304af63cd2a2f873e36260616289e827b1e","head":{"label":"PieselBois:quasilyte/type_guard_v1","ref":"quasilyte/type_guard_v1","sha":"8aee9304af63cd2a2f873e36260616289e827b1e","user":{"login":"PieselBois","id":36415830,"avatar_url":"https://avatars3.githubusercontent.com/u/36415830?v=4","gravatar_id":"","url":"https://api.github.com/users/PieselBois","html_url":"https://github.com/PieselBois","followers_url":"https://api.github.com/users/PieselBois/followers","following_url":"https://api.github.com/users/PieselBois/following{/other_user}","gists_url":"https://api.github.com/users/PieselBois/gists{/gist_id}","starred_url":"https://api.github.com/users/PieselBois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PieselBois/subscriptions","organizations_url":"https://api.github.com/users/PieselBois/orgs","repos_url":"https://api.github.com/users/PieselBois/repos","events_url":"https://api.github.com/users/PieselBois/events{/privacy}","received_events_url":"https://api.github.com/users/PieselBois/received_events","type":"Organization","site_admin":false},"repo":{"id":132232698,"name":"kfulint","full_name":"PieselBois/kfulint","owner":{"login":"PieselBois","id":36415830,"avatar_url":"https://avatars3.githubusercontent.com/u/36415830?v=4","gravatar_id":"","url":"https://api.github.com/users/PieselBois","html_url":"https://github.com/PieselBois","followers_url":"https://api.github.com/users/PieselBois/followers","following_url":"https://api.github.com/users/PieselBois/following{/other_user}","gists_url":"https://api.github.com/users/PieselBois/gists{/gist_id}","starred_url":"https://api.github.com/users/PieselBois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PieselBois/subscriptions","organizations_url":"https://api.github.com/users/PieselBois/orgs","repos_url":"https://api.github.com/users/PieselBois/repos","events_url":"https://api.github.com/users/PieselBois/events{/privacy}","received_events_url":"https://api.github.com/users/PieselBois/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/PieselBois/kfulint","description":"Go source code linter.","fork":false,"url":"https://api.github.com/repos/PieselBois/kfulint","forks_url":"https://api.github.com/repos/PieselBois/kfulint/forks","keys_url":"https://api.github.com/repos/PieselBois/kfulint/keys{/key_id}","collaborators_url":"https://api.github.com/repos/PieselBois/kfulint/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/PieselBois/kfulint/teams","hooks_url":"https://api.github.com/repos/PieselBois/kfulint/hooks","issue_events_url":"https://api.github.com/repos/PieselBois/kfulint/issues/events{/number}","events_url":"https://api.github.com/repos/PieselBois/kfulint/events","assignees_url":"https://api.github.com/repos/PieselBois/kfulint/assignees{/user}","branches_url":"https://api.github.com/repos/PieselBois/kfulint/branches{/branch}","tags_url":"https://api.github.com/repos/PieselBois/kfulint/tags","blobs_url":"https://api.github.com/repos/PieselBois/kfulint/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/PieselBois/kfulint/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/PieselBois/kfulint/git/refs{/sha}","trees_url":"https://api.github.com/repos/PieselBois/kfulint/git/trees{/sha}","statuses_url":"https://api.github.com/repos/PieselBois/kfulint/statuses/{sha}","languages_url":"https://api.github.com/repos/PieselBois/kfulint/languages","stargazers_url":"https://api.github.com/repos/PieselBois/kfulint/stargazers","contributors_url":"https://api.github.com/repos/PieselBois/kfulint/contributors","subscribers_url":"https://api.github.com/repos/PieselBois/kfulint/subscribers","subscription_url":"https://api.github.com/repos/PieselBois/kfulint/subscription","commits_url":"https://api.github.com/repos/PieselBois/kfulint/commits{/sha}","git_commits_url":"https://api.github.com/repos/PieselBois/kfulint/git/commits{/sha}","comments_url":"https://api.github.com/repos/PieselBois/kfulint/comments{/number}","issue_comment_url":"https://api.github.com/repos/PieselBois/kfulint/issues/comments{/number}","contents_url":"https://api.github.com/repos/PieselBois/kfulint/contents/{+path}","compare_url":"https://api.github.com/repos/PieselBois/kfulint/compare/{base}...{head}","merges_url":"https://api.github.com/repos/PieselBois/kfulint/merges","archive_url":"https://api.github.com/repos/PieselBois/kfulint/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/PieselBois/kfulint/downloads","issues_url":"https://api.github.com/repos/PieselBois/kfulint/issues{/number}","pulls_url":"https://api.github.com/repos/PieselBois/kfulint/pulls{/number}","milestones_url":"https://api.github.com/repos/PieselBois/kfulint/milestones{/number}","notifications_url":"https://api.github.com/repos/PieselBois/kfulint/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/PieselBois/kfulint/labels{/name}","releases_url":"https://api.github.com/repos/PieselBois/kfulint/releases{/id}","deployments_url":"https://api.github.com/repos/PieselBois/kfulint/deployments","created_at":"2018-05-05T09:17:26Z","updated_at":"2018-05-08T22:31:02Z","pushed_at":"2018-05-08T22:31:59Z","git_url":"git://github.com/PieselBois/kfulint.git","ssh_url":"git@github.com:PieselBois/kfulint.git","clone_url":"https://github.com/PieselBois/kfulint.git","svn_url":"https://github.com/PieselBois/kfulint","homepage":"","size":22,"stargazers_count":3,"watchers_count":3,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":15,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":0,"open_issues":15,"watchers":3,"default_branch":"master"}},"base":{"label":"PieselBois:master","ref":"master","sha":"515f85c5299009609fe39fcc9746a5a3a6e7a604","user":{"login":"PieselBois","id":36415830,"avatar_url":"https://avatars3.githubusercontent.com/u/36415830?v=4","gravatar_id":"","url":"https://api.github.com/users/PieselBois","html_url":"https://github.com/PieselBois","followers_url":"https://api.github.com/users/PieselBois/followers","following_url":"https://api.github.com/users/PieselBois/following{/other_user}","gists_url":"https://api.github.com/users/PieselBois/gists{/gist_id}","starred_url":"https://api.github.com/users/PieselBois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PieselBois/subscriptions","organizations_url":"https://api.github.com/users/PieselBois/orgs","repos_url":"https://api.github.com/users/PieselBois/repos","events_url":"https://api.github.com/users/PieselBois/events{/privacy}","received_events_url":"https://api.github.com/users/PieselBois/received_events","type":"Organization","site_admin":false},"repo":{"id":132232698,"name":"kfulint","full_name":"PieselBois/kfulint","owner":{"login":"PieselBois","id":36415830,"avatar_url":"https://avatars3.githubusercontent.com/u/36415830?v=4","gravatar_id":"","url":"https://api.github.com/users/PieselBois","html_url":"https://github.com/PieselBois","followers_url":"https://api.github.com/users/PieselBois/followers","following_url":"https://api.github.com/users/PieselBois/following{/other_user}","gists_url":"https://api.github.com/users/PieselBois/gists{/gist_id}","starred_url":"https://api.github.com/users/PieselBois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PieselBois/subscriptions","organizations_url":"https://api.github.com/users/PieselBois/orgs","repos_url":"https://api.github.com/users/PieselBois/repos","events_url":"https://api.github.com/users/PieselBois/events{/privacy}","received_events_url":"https://api.github.com/users/PieselBois/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/PieselBois/kfulint","description":"Go source code linter.","fork":false,"url":"https://api.github.com/repos/PieselBois/kfulint","forks_url":"https://api.github.com/repos/PieselBois/kfulint/forks","keys_url":"https://api.github.com/repos/PieselBois/kfulint/keys{/key_id}","collaborators_url":"https://api.github.com/repos/PieselBois/kfulint/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/PieselBois/kfulint/teams","hooks_url":"https://api.github.com/repos/PieselBois/kfulint/hooks","issue_events_url":"https://api.github.com/repos/PieselBois/kfulint/issues/events{/number}","events_url":"https://api.github.com/repos/PieselBois/kfulint/events","assignees_url":"https://api.github.com/repos/PieselBois/kfulint/assignees{/user}","branches_url":"https://api.github.com/repos/PieselBois/kfulint/branches{/branch}","tags_url":"https://api.github.com/repos/PieselBois/kfulint/tags","blobs_url":"https://api.github.com/repos/PieselBois/kfulint/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/PieselBois/kfulint/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/PieselBois/kfulint/git/refs{/sha}","trees_url":"https://api.github.com/repos/PieselBois/kfulint/git/trees{/sha}","statuses_url":"https://api.github.com/repos/PieselBois/kfulint/statuses/{sha}","languages_url":"https://api.github.com/repos/PieselBois/kfulint/languages","stargazers_url":"https://api.github.com/repos/PieselBois/kfulint/stargazers","contributors_url":"https://api.github.com/repos/PieselBois/kfulint/contributors","subscribers_url":"https://api.github.com/repos/PieselBois/kfulint/subscribers","subscription_url":"https://api.github.com/repos/PieselBois/kfulint/subscription","commits_url":"https://api.github.com/repos/PieselBois/kfulint/commits{/sha}","git_commits_url":"https://api.github.com/repos/PieselBois/kfulint/git/commits{/sha}","comments_url":"https://api.github.com/repos/PieselBois/kfulint/comments{/number}","issue_comment_url":"https://api.github.com/repos/PieselBois/kfulint/issues/comments{/number}","contents_url":"https://api.github.com/repos/PieselBois/kfulint/contents/{+path}","compare_url":"https://api.github.com/repos/PieselBois/kfulint/compare/{base}...{head}","merges_url":"https://api.github.com/repos/PieselBois/kfulint/merges","archive_url":"https://api.github.com/repos/PieselBois/kfulint/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/PieselBois/kfulint/downloads","issues_url":"https://api.github.com/repos/PieselBois/kfulint/issues{/number}","pulls_url":"https://api.github.com/repos/PieselBois/kfulint/pulls{/number}","milestones_url":"https://api.github.com/repos/PieselBois/kfulint/milestones{/number}","notifications_url":"https://api.github.com/repos/PieselBois/kfulint/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/PieselBois/kfulint/labels{/name}","releases_url":"https://api.github.com/repos/PieselBois/kfulint/releases{/id}","deployments_url":"https://api.github.com/repos/PieselBois/kfulint/deployments","created_at":"2018-05-05T09:17:26Z","updated_at":"2018-05-08T22:31:02Z","pushed_at":"2018-05-08T22:31:59Z","git_url":"git://github.com/PieselBois/kfulint.git","ssh_url":"git@github.com:PieselBois/kfulint.git","clone_url":"https://github.com/PieselBois/kfulint.git","svn_url":"https://github.com/PieselBois/kfulint","homepage":"","size":22,"stargazers_count":3,"watchers_count":3,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":15,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":0,"open_issues":15,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/PieselBois/kfulint/pulls/19"},"html":{"href":"https://github.com/PieselBois/kfulint/pull/19"},"issue":{"href":"https://api.github.com/repos/PieselBois/kfulint/issues/19"},"comments":{"href":"https://api.github.com/repos/PieselBois/kfulint/issues/19/comments"},"review_comments":{"href":"https://api.github.com/repos/PieselBois/kfulint/pulls/19/comments"},"review_comment":{"href":"https://api.github.com/repos/PieselBois/kfulint/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/PieselBois/kfulint/pulls/19/commits"},"statuses":{"href":"https://api.github.com/repos/PieselBois/kfulint/statuses/8aee9304af63cd2a2f873e36260616289e827b1e"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"Quasilyte"}}