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

Panic case #40

Open
Antonboom opened this issue Mar 15, 2022 · 5 comments
Open

Panic case #40

Antonboom opened this issue Mar 15, 2022 · 5 comments

Comments

@Antonboom
Copy link

Hi!

Please look at this simple example:

func didPanic(fn func()) (result bool) {
	result = true // Handle `panic(nil)` case.
	defer func() { recover() }()
	fn()
	return false
}
assigned to result, but reassigned without using the value (wastedassign)
        result = true
        ^
@sanposhiho
Copy link
Owner

Not bug. Your case should be written like below to avoid the warn from wastedassign.

func didPanic(fn func()) (result bool) {
	defer func() {
		result = true 
		recover()
	}()
	fn()
	return false
}

@Antonboom
Copy link
Author

Antonboom commented Mar 16, 2022

@sanposhiho it's absolutely different code :) it doesn't work properly for not panic case:
https://goplay.tools/snippet/mrRqJbl_pTP

package main

import "fmt"

func main() {
	fmt.Println(didPanic(func() {}))                 // Must be false.
	fmt.Println(didPanic(func() { panic("boom!") })) // Must be true.
	fmt.Println(didPanic(func() { panic(nil) }))     // Must be true.
}

func didPanic(fn func()) (result bool) {
	defer func() {
		result = true
		recover()
	}()
	fn()
	return false
}

/*
true
true
true
*/

@nickajacks1
Copy link

@Antonboom recover() returns the value passed to panic(), so you can check that to conditionally set result to true.
https://go.dev/blog/defer-panic-and-recover

	defer func() {
		if r := recover(); r != nil {
			result = true
		}
	}()

@Antonboom
Copy link
Author

Antonboom commented Jan 23, 2024

@nickajacks1 hi!

Thank you for your attention, but your code is not valid for Go < 1.21, because I can do panic(nil).

Anyway the question is not "how to rewrite the code", but "how to improve the linter".

@nickajacks1
Copy link

I think I understand now. If fn panics, the assignment in the original code is technically used. I'm not really familiar with Go's AST, but this sounds complex to statically determine. Some thoughts:

  • since fn is not first nil-checked, calling it can panic, so we can statically determine that it could panic.
    • Otherwise, I assume that it is impossible to know if fn panics without looking at every single usage of didPanic
  • wastedassign could theoretically check for calls to recover within a defer function and assume that any called function object can panic. This could lead to false-negatives.
    • I noticed that ineffassign has opted for this route, though it disclaims in the readme that it opts for false-negatives over false-positives.
  • You could tell wastedassign that a function could panic with some annotation comment, but that's no better than using a comment to disable the lint warning on the variable assignment.
  • panic(nil) is probably dangerous anyway (sorry to pick on the example again...)

So I'm not really sure what the best move here is. I've noticed other times where wastedassign seems to opt for false-positives over false-negatives:

// wastedassign gives a "false positive" here (or maybe it's a bug, I haven't really looked deeper)
thing := 0
switch arg {
case 1:
	thing = 3
case 2:
	thing = 39
default:
	thing = 100
}

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

No branches or pull requests

3 participants