Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

無駄な代入文を発見する #1

Merged
merged 14 commits into from
Sep 4, 2020
Merged

Conversation

sanposhiho
Copy link
Owner

@sanposhiho sanposhiho commented Sep 2, 2020

以下に対して警告を行う

  • 再代入された後にその値が使われることなく、別の値に書き換わっている
  • 再代入された後にその値が使われず関数が終了する

その他

  • github actionsでのテストの導入

レビュー頂きたい事

自分ではエッジケースが思い付かず、かっっっなりのシンプルな実装になってしまいました何かこの実装で引っかからない特殊なパターンなどありましたら教えていただきたいです🙇‍♂️

Copy link

@gasugesu gasugesu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

条件分岐への対応が抜けているようです

wastedassign.go Outdated
s := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)

for _, sf := range s.SrcFuncs {
for _, local := range sf.Locals {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naiveモードでビルドしないと厳しくないですか?

Copy link
Owner Author

@sanposhiho sanposhiho Sep 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildssaをforkしてforkしたrepositoryでNaiveモードに変更しています🙏

https://github.com/sanposhiho/tools

@sanposhiho
Copy link
Owner Author

@gasugesu

条件分岐への対応が抜けているようです

	useOutOfIf := 0 // want "wasted assignment"
	ifBool := false
	if ifBool {
		useOutOfIf = 10 // want "reassigned, but never used afterwards" が出て欲しいけど、"wasted assignment"が出る
		return
	}
	useOutOfIf = 12
	println(useOutOfIf)
	return

のパターンであっていますか…?(↑直します

@gasugesu
Copy link

gasugesu commented Sep 3, 2020

@gasugesu

条件分岐への対応が抜けているようです

	useOutOfIf := 0 // want "wasted assignment"
	ifBool := false
	if ifBool {
		useOutOfIf = 10 // want "reassigned, but never used afterwards" が出て欲しいけど、"wasted assignment"が出る
		return
	}
	useOutOfIf = 12
	println(useOutOfIf)
	return

のパターンであっていますか…?(↑直します

そうですね!
僕もまさにこのif elseのパターンで悩んでいるのですが、複数のブロックから構成される場合が多少面倒そうです

@@ -0,0 +1,45 @@
name: test_and_lint
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GitHub Actionsで自動でチェックするのいいですね!
reviewdog というツールを使って自動で指摘がある箇所にコメントをつけられるツールもあるので導入してみるとより便利になります
http://haya14busa.com/reviewdog/

@sanposhiho
Copy link
Owner Author

@gasugesu

あっ、と言うか内容被っちゃってたんですね…(今気がついた

wastedassign.go Outdated
if noNextSuccs {
return noUseUntilReturn
} else {
return notWasted
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

いい指摘!

@110y
Copy link

110y commented Sep 3, 2020

test caseを少しいじって

package a

func f() {
	useOutOfIf := 0 // want "wasted assignment"
	if false {
		useOutOfIf = 10 // want "reassigned, but never used afterwards"
		useOutOfIf = 10 // want "reassigned, but never used afterwards" // <= added

		return
	}
	useOutOfIf = 12
	println(useOutOfIf)
	return
}

のようにすると

--- FAIL: TestAnalyzer (0.01s)
    analysistest.go:419: a/a.go:6:3: diagnostic "wasted assignment" does not match pattern "reassigned, but never used afterwards"
    analysistest.go:419: a/a.go:7:3: diagnostic "wasted assignment" does not match pattern "reassigned, but never used afterwards"
    analysistest.go:483: a/a.go:6: no diagnostic was reported matching "reassigned, but never used afterwards"
    analysistest.go:483: a/a.go:7: no diagnostic was reported matching "reassigned, but never used afterwards"
FAIL
FAIL	github.com/sanposhiho/wastedassign	0.019s
?   	github.com/sanposhiho/wastedassign/cmd/wastedassign	[no test files]
?   	github.com/sanposhiho/wastedassign/plugin	[no test files]
FAIL

のような形でテストが落ちるのですが、これは想定通りですか?
追加した useOutOfIf = 10reassigned, but never used afterwards で警告されるべきなのかなと思いました。

@sanposhiho
Copy link
Owner Author

sanposhiho commented Sep 3, 2020

@110y san
レビューありがとうございます!

package a

func f() {
	useOutOfIf := 0 // want "wasted assignment"
	if false {
		useOutOfIf = 10 // want "wasted assignment"  // <= fix
		useOutOfIf = 10 // want "reassigned, but never used afterwards" // <= added

		return
	}
	useOutOfIf = 12
	println(useOutOfIf)
	return
}

テストケースは正しくはこうなります!

wasted assignment: 代入後にその値を使うことなく別の値に書き換えられている
reassigned, but never used afterwards: 代入後にその値を使うことなく関数がreturn

と言った使い分けになっています

が、どちらにしろ追加していただいた部分でエラーが出るのは間違っていたので修正しました🙇‍♂️

@sanposhiho sanposhiho merged commit 0cf5b0d into master Sep 4, 2020
@gasugesu
Copy link

gasugesu commented Sep 4, 2020

close後に後出しですみません
このテストケースだと

  • elseの中のretが間違ってret declared but not usedと報告される
  • analyzerが途中で落ちる
    ようです.
    ちなみにreturn の直前のuseOutOfIf += ...に報告がなされないのは想定通りですか?

(報告用のwantを正しく修正できてないですがよろしくお願いします)

func p(x int) int {
	return x + 1
}

func f(param int) int {
	useOutOfIf := 0
	ret := 0
	if false {
		useOutOfIf = 10 // want "wasted assignment"
		useOutOfIf = 10 // want "reassigned, but never used afterwards"

		return 0
	} else if param == 100 {
		ret = useOutOfIf
	} else if param == 200 {
		useOutOfIf = 100
		useOutOfIf = 100
		useOutOfIf = p(useOutOfIf)
		useOutOfIf += 200
	} else {
		ret := 100
	}
	useOutOfIf = 12
	println(useOutOfIf)
	useOutOfIf = 192 // want "reassigned, but never used afterwards"
	useOutOfIf += 100
	useOutOfIf += 200
	return ret
}

@sanposhiho
Copy link
Owner Author

sanposhiho commented Sep 4, 2020

@gasugesu

コメントありがとうございます!

elseの中のretが間違ってret declared but not usedと報告される

はwastedassignが出力しているエラーではないです(goが出している物)(else句のscopeにretを:=で新たに定義したけど使われてないやんけの意味

なのでそもそもtestcaseがgoのコード的によくないです

なので正しいtestcaseは(ついでにwantも付与して)こうなると思います

package a

func p(x int) int {
	return x + 1
}

func f(param int) int {
	useOutOfIf := 0 // want "wasted assignment"
	ret := 0        // want "wasted assignment"
	if false {
		useOutOfIf = 10 // want "wasted assignment"
		useOutOfIf = 10 // want "reassigned, but never used afterwards"

		return 0
	} else if param == 100 {
		ret = useOutOfIf
	} else if param == 200 {
		useOutOfIf = 100 // want "wasted assignment"
		useOutOfIf = 100
		useOutOfIf = p(useOutOfIf)
		useOutOfIf += 200 // want "reassigned, but never used afterwards"
	}
	useOutOfIf = 12
	println(useOutOfIf)
	useOutOfIf = 192
	useOutOfIf += 100
	useOutOfIf += 200 // want "reassigned, but never used afterwards"
	return ret
}

これなら通ります

ちなみにreturn の直前のuseOutOfIf += ...に報告がなされないのは想定通りですか?

これに関してですが上のテストケースで"reassigned, but never used afterwards"がちゃんと出力されると思います

長くなりましたが、以上から総じてこのtestcaseは正しく動作している気がします🙏

@sanposhiho
Copy link
Owner Author

@gasugesu
詳しいテストケースとてもありがたいです
他に気になる点があれば是非お願いします!🙏

僕としては上のテストケースだと

ret := 0        // want "wasted assignment"

が怪しい感じを感じ取っています(returnでしか使ってないしwastedではあるが…と言った感じ

@gasugesu
Copy link

gasugesu commented Sep 4, 2020

Oh... ret := ..は僕のミスです
正しいテストケースはこちらになります。。。
コードの中にコメント追加しましたが、func f() の一番最初に定義されている変数2個についてどちらも正しく報告されていないような気がしています

package a

func p(x int) int {
	return x + 1
}

func f(param int) int {
	useOutOfIf := 0
	// このretは条件分岐によっては最後のretで使用されるが wasted assignmentと表示される
	ret := 0
	if false {
		useOutOfIf = 10 // want "wasted assignment"
		useOutOfIf = 10 // want "reassigned, but never used afterwards"

		return 0
	} else if param == 100 {
		// 8行目のuseOutOfIfはここで使われるが8行目でwasted assignmentと出ている
		ret = useOutOfIf
	} else if param == 200 {
		useOutOfIf = 100
		useOutOfIf = 100
		useOutOfIf = p(useOutOfIf)
		useOutOfIf += 200
	} else {
                  // ここも使用されていない系のエラー出る
		ret = 100
	}
	useOutOfIf = 12
	println(useOutOfIf)
	useOutOfIf = 192 // want "reassigned, but never used afterwards"
	useOutOfIf += 100
	useOutOfIf += 200
	return ret
}

@sanposhiho
Copy link
Owner Author

sanposhiho commented Sep 4, 2020

@gasugesu

大きく分けて

  • returnに使用されているのを使用されているとみなすか
  • 別の値へのStoreに使用されているのを使用されているとみなすか

という問題だと思っていて

両方使用しているとみなすべきな気がしてきましたね…

前者は関数からの結果をreturnするというのは明らかに存在するケースですし、後者も色んなスコープで色んな変数に同じ値を定義する際など使用することがありそうです

issueに立てておきます!ありがとうございます🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants