𝐖𝐡𝐚𝐭 𝐢𝐬 𝕘𝕠-𝕥𝕣𝕪
A source to source translator for error-propagating in golang.
The implementation of Proposal: A built-in Go error check function, try.
Quick Start
Create source files ending with _try.go
/ _try_test.go
.
Build tag //go:build try
required.
Then go generate -tags try ./...
(or run by IDE whatever).
And it is a good idea to switch custom build tag to try
when working in goland or vscode,
so IDE will be happy to index and check your code.
Example
Create file copyfile_try.go
.
//go:build try
//go:generate go install github.com/goghcrow/go-try/cmd/trygen@main
//go:generate trygen
package example
import (
"fmt"
"io"
"os"
. "github.com/goghcrow/go-try"
)
// CopyFile example
// from https://github.com/golang/proposal/blob/master/design/32437-try-builtin.md#examples
//
//goland:noinspection GoUnhandledErrorResult
func CopyFile(src, dst string) (err error) {
defer HandleErrorf(&err, "copy %s %s", src, dst)
r := Try(os.Open(src))
defer r.Close()
w := Try(os.Create(dst))
defer func() {
w.Close()
if err != nil {
os.Remove(dst) // only if a “try” fails
}
}()
Try(io.Copy(w, r))
Try0(w.Close())
return nil
}
func HandleErrorf(err *error, format string, args ...any) {
if *err != nil {
*err = fmt.Errorf(format+": %v", append(args, *err)...)
}
}
run go:generate and output copyfile.go
.
//go:build !try
// Code generated by github.com/goghcrow/go-try DO NOT EDIT.
package example
import (
"fmt"
"io"
"os"
)
func CopyFile(src, dst string) (err error) {
defer HandleErrorf(&err, "copy %s %s", src, dst)
𝘃𝗮𝗹𝟭, 𝗲𝗿𝗿𝟭 := os.Open(src)
if 𝗲𝗿𝗿𝟭 != nil {
err = 𝗲𝗿𝗿𝟭
return
}
r := 𝘃𝗮𝗹𝟭
defer r.Close()
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟮 := os.Create(dst)
if 𝗲𝗿𝗿𝟮 != nil {
err = 𝗲𝗿𝗿𝟮
return
}
w := 𝘃𝗮𝗹𝟮
defer func() {
w.Close()
if err != nil {
os.Remove(dst)
}
}()
_, 𝗲𝗿𝗿𝟯 := io.Copy(w, r)
if 𝗲𝗿𝗿𝟯 != nil {
err = 𝗲𝗿𝗿𝟯
return
}
𝗲𝗿𝗿𝟰 := w.Close()
if 𝗲𝗿𝗿𝟰 != nil {
err = 𝗲𝗿𝗿𝟰
return
}
return nil
}
func HandleErrorf(err *error, format string, args ...any) {
if *err != nil {
*err = fmt.Errorf(format+": %v", append(args, *err)...)
}
}
Translating Examples
prelude
package test
func func1[A, R any](a A) (_ R, _ error) { return }
func return1[A any]() (_ A) { return }
func ret0Err() (_ error) { return }
func ret1Err[A any]() (_ A, _ error) { return }
func ret2Err[A, B any]() (_ A, _ B, _ error) { return }
func ret3Err[A, B, C any]() (_ A, _ B, _ C, _ error) { return }
func consume1[A any](A) {}
func consume2[A, B any](A, B) {}
func consume3[A, B, C any](A, B, C) {}
func id[A any](a A) A { return a }
func handleErrorf(err *error, format string, args ...interface{}) {
if *err != nil {
*err = fmt.Errorf(format+": %v", append(args, *err)...)
}
}
Error Handling
Before
|
After
|
func error_wrapping() (a int, err error) {
defer handleErrorf(&err, "something wrong")
Try(ret1Err[bool]())
a = 42
return
}
|
func error_wrapping() (a int, err error) {
defer handleErrorf(&err, "something wrong")
_, 𝗲𝗿𝗿𝟭 := ret1Err[bool]()
if 𝗲𝗿𝗿𝟭 != nil {
err = 𝗲𝗿𝗿𝟭
return
}
a = 42
return
}
|
Order of Evaluation
Before
|
After
|
type (
A = int
B = int
C = int
)
println(
[]func(int) int{}[id[int](0)+Try(ret1Err[A]())](
id[int](1)+Try(ret1Err[B]()),
) + Try(ret1Err[C]()) + id[int](2),
)
|
𝘃𝗮𝗹𝟭 := id[int](0)
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟭 := ret1Err[A]()
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
𝘃𝗮𝗹𝟯 := []func(int) int{}[𝘃𝗮𝗹𝟭+𝘃𝗮𝗹𝟮]
𝘃𝗮𝗹𝟰 := id[int](1)
𝘃𝗮𝗹𝟱, 𝗲𝗿𝗿𝟮 := ret1Err[B]()
if 𝗲𝗿𝗿𝟮 != nil {
return 𝗲𝗿𝗿𝟮
}
𝘃𝗮𝗹𝟲 := 𝘃𝗮𝗹𝟯(𝘃𝗮𝗹𝟰 + 𝘃𝗮𝗹𝟱)
𝘃𝗮𝗹𝟳, 𝗲𝗿𝗿𝟯 := ret1Err[C]()
if 𝗲𝗿𝗿𝟯 != nil {
return 𝗲𝗿𝗿𝟯
}
𝘃𝗮𝗹𝟴 := id[int](2)
println(𝘃𝗮𝗹𝟲 + 𝘃𝗮𝗹𝟳 + 𝘃𝗮𝗹𝟴)
|
Logical Operator Or
Before
|
After
|
_ = id(true) || Try(func1[int, bool](2)) || id(false)
|
𝘃𝗮𝗹𝟭 := id(true)
if !𝘃𝗮𝗹𝟭 {
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟭 := func1[int, bool](2)
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
𝘃𝗮𝗹𝟭 = 𝘃𝗮𝗹𝟮
}
if !𝘃𝗮𝗹𝟭 {
𝘃𝗮𝗹𝟭 = id(false)
}
_ = 𝘃𝗮𝗹𝟭
|
Logical Operator And
Before
|
After
|
_ = id(true) && Try(func1[int, bool](2)) && id(false)
|
𝘃𝗮𝗹𝟭 := id(true)
if 𝘃𝗮𝗹𝟭 {
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟭 := func1[int, bool](2)
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
𝘃𝗮𝗹𝟭 = 𝘃𝗮𝗹𝟮
}
if 𝘃𝗮𝗹𝟭 {
𝘃𝗮𝗹𝟭 = id(false)
}
_ = 𝘃𝗮𝗹𝟭
|
If Stmt
Before
|
After
|
if Try(func1[int, bool](1)) {
} else if false {
} else if a := Try(func1[int, bool](2)); a {
} else if Try(func1[int, bool](3)) {
} else if true {
}
|
𝘃𝗮𝗹𝟭, 𝗲𝗿𝗿𝟭 := func1[int, bool](1)
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
if 𝘃𝗮𝗹𝟭 {
} else if false {
} else {
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟮 := func1[int, bool](2)
if 𝗲𝗿𝗿𝟮 != nil {
return 𝗲𝗿𝗿𝟮
}
if a := 𝘃𝗮𝗹𝟮; a {
} else {
𝘃𝗮𝗹𝟯, 𝗲𝗿𝗿𝟯 := func1[int, bool](3)
if 𝗲𝗿𝗿𝟯 != nil {
return 𝗲𝗿𝗿𝟯
}
if 𝘃𝗮𝗹𝟯 {
} else if true {
}
}
}
|
For Stmt
Before
|
After
|
for i := Try(ret1Err[A]());
Try(func1[int, bool](i)); Try(func1[A, C](i)) {
println(i)
}
|
𝘃𝗮𝗹𝟭, 𝗲𝗿𝗿𝟭 := ret1Err[A]()
if 𝗲𝗿𝗿𝟭 != nil {
err = 𝗲𝗿𝗿𝟭
return
}
for i := 𝘃𝗮𝗹𝟭; ; {
𝗽𝗼𝘀𝘁𝟭 := func() (_ E𝗿𝗿𝗼𝗿) {
_, 𝗲𝗿𝗿𝟭 := func1[A, C](i)
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
return
}
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟮 := func1[int, bool](i)
if 𝗲𝗿𝗿𝟮 != nil {
return 𝗲𝗿𝗿𝟮
}
if !𝘃𝗮𝗹𝟮 {
break
}
println(i)
𝗲𝗿𝗿𝟯 := 𝗽𝗼𝘀𝘁𝟭()
if 𝗲𝗿𝗿𝟯 != nil {
return 𝗲𝗿𝗿𝟯
}
}
|
Range Stmt
Before
|
After
|
Outer:
for range Try(ret1Err[[]int]()) {
Inner:
for range Try(ret1Err[[]string]()) {
switch a {
case 1:
goto Inner
case 2:
goto Outer
case 3:
break Inner
case 4:
break Outer
case 5:
continue Inner
case 6:
continue Outer
}
}
}
|
𝗟_𝗚𝗼𝘁𝗼_𝗢𝘂𝘁𝗲𝗿𝟭:
{
𝘃𝗮𝗹𝟭, 𝗲𝗿𝗿𝟭 := ret1Err[[]int]()
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
Outer:
for range 𝘃𝗮𝗹𝟭 {
𝗟_𝗚𝗼𝘁𝗼_𝗜𝗻𝗻𝗲𝗿𝟭:
{
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟮 := ret1Err[[]string]()
if 𝗲𝗿𝗿𝟮 != nil {
return 𝗲𝗿𝗿𝟮
}
Inner:
for range 𝘃𝗮𝗹𝟮 {
switch a {
case 1:
goto 𝗟_𝗚𝗼𝘁𝗼_𝗜𝗻𝗻𝗲𝗿𝟭
case 2:
goto 𝗟_𝗚𝗼𝘁𝗼_𝗢𝘂𝘁𝗲𝗿𝟭
case 3:
break Inner
case 4:
break Outer
case 5:
continue Inner
case 6:
continue Outer
}
}
}
}
}
|
Switch Stmt
Before
|
After
|
switch i := Try(func1[int, A](0)); Try(func1[int, A](i)) {
case Try(func1[int, B](i)):
println("B")
case id[int](i):
println("C")
case Try(func1[int, D](i)):
println("D1")
case id[int](i):
println("E")
case Try(func1[int, D](i)):
println("D2")
default:
println("default")
}
|
{
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟭 := func1[int, A](0)
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
i := 𝘃𝗮𝗹𝟮
𝘃𝗮𝗹𝟯, 𝗲𝗿𝗿𝟮 := func1[int, A](i)
if 𝗲𝗿𝗿𝟮 != nil {
return 𝗲𝗿𝗿𝟮
}
𝘃𝗮𝗹𝟭 := 𝘃𝗮𝗹𝟯
𝘃𝗮𝗹𝟰, 𝗲𝗿𝗿𝟯 := func1[int, B](i)
if 𝗲𝗿𝗿𝟯 != nil {
return 𝗲𝗿𝗿𝟯
}
if 𝘃𝗮𝗹𝟭 == 𝘃𝗮𝗹𝟰 {
println("B")
} else if 𝘃𝗮𝗹𝟭 == id[int](i) {
println("C")
} else {
𝘃𝗮𝗹𝟱, 𝗲𝗿𝗿𝟰 := func1[int, D](i)
if 𝗲𝗿𝗿𝟰 != nil {
return 𝗲𝗿𝗿𝟰
}
if 𝘃𝗮𝗹𝟭 == 𝘃𝗮𝗹𝟱 {
println("D1")
} else if 𝘃𝗮𝗹𝟭 == id[int](i) {
println("E")
} else {
𝘃𝗮𝗹𝟲, 𝗲𝗿𝗿𝟱 := func1[int, D](i)
if 𝗲𝗿𝗿𝟱 != nil {
return 𝗲𝗿𝗿𝟱
}
if 𝘃𝗮𝗹𝟭 == 𝘃𝗮𝗹𝟲 {
println("D2")
} else {
println("default")
}
}
}
}
|
Switch Stmt
Before
|
After
|
outer:
switch {
case Try(func1[int, A](1)) == 42:
println("outer")
inner:
switch {
case Try(func1[int, B](1)) == 42:
break inner
case Try(func1[int, C](1)) == 42:
goto inner
case Try(func1[int, D](1)) == 42:
println("inner")
break outer
case Try(func1[int, E](1)) == 42:
println("inner")
goto outer
}
default:
println("default")
}
|
outer:
{
𝘃𝗮𝗹𝟭, 𝗲𝗿𝗿𝟭 := func1[int, A](1)
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
if 𝘃𝗮𝗹𝟭 == 42 {
println("outer")
inner:
{
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟮 := func1[int, B](1)
if 𝗲𝗿𝗿𝟮 != nil {
return 𝗲𝗿𝗿𝟮
}
if 𝘃𝗮𝗹𝟮 == 42 {
goto 𝗟_𝗕𝗿𝗸𝗧𝗼_𝗶𝗻𝗻𝗲𝗿𝟭
} else {
𝘃𝗮𝗹𝟯, 𝗲𝗿𝗿𝟯 := func1[int, C](1)
if 𝗲𝗿𝗿𝟯 != nil {
return 𝗲𝗿𝗿𝟯
}
if 𝘃𝗮𝗹𝟯 == 42 {
goto inner
} else {
𝘃𝗮𝗹𝟰, 𝗲𝗿𝗿𝟰 := func1[int, D](1)
if 𝗲𝗿𝗿𝟰 != nil {
return 𝗲𝗿𝗿𝟰
}
if 𝘃𝗮𝗹𝟰 == 42 {
println("inner")
goto 𝗟_𝗕𝗿𝗸𝗧𝗼_𝗼𝘂𝘁𝗲𝗿𝟭
} else {
𝘃𝗮𝗹𝟱, 𝗲𝗿𝗿𝟱 := func1[int, E](1)
if 𝗲𝗿𝗿𝟱 != nil {
return 𝗲𝗿𝗿𝟱
}
if 𝘃𝗮𝗹𝟱 == 42 {
println("inner")
goto outer
}
}
}
}
𝗟_𝗕𝗿𝗸𝗧𝗼_𝗶𝗻𝗻𝗲𝗿𝟭:
}
} else {
println("default")
}
𝗟_𝗕𝗿𝗸𝗧𝗼_𝗼𝘂𝘁𝗲𝗿𝟭:
}
|
Switch Stmt
Before
|
After
|
type (
A = int
B = int
C = int
D = int
E = int
F = int
)
switch i {
case Try(ret1Err[A]()):
Try(ret1Err[B]())
fallthrough
case Try(ret1Err[C]()):
Try(ret1Err[D]())
fallthrough
case Try(ret1Err[E]()):
Try(ret1Err[F]())
}
|
type (
A = int
B = int
C = int
D = int
E = int
F = int
)
{
𝘃𝗮𝗹𝟭 := i
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟭 := ret1Err[A]()
if 𝗲𝗿𝗿𝟭 != nil {
err = 𝗲𝗿𝗿𝟭
return
}
if 𝘃𝗮𝗹𝟭 == 𝘃𝗮𝗹𝟮 {
{
_, 𝗲𝗿𝗿𝟮 := ret1Err[B]()
if 𝗲𝗿𝗿𝟮 != nil {
err = 𝗲𝗿𝗿𝟮
return
}
}
{
_, 𝗲𝗿𝗿𝟯 := ret1Err[D]()
if 𝗲𝗿𝗿𝟯 != nil {
err = 𝗲𝗿𝗿𝟯
return
}
}
{
_, 𝗲𝗿𝗿𝟰 := ret1Err[F]()
if 𝗲𝗿𝗿𝟰 != nil {
err = 𝗲𝗿𝗿𝟰
return
}
}
} else {
𝘃𝗮𝗹𝟯, 𝗲𝗿𝗿𝟱 := ret1Err[C]()
if 𝗲𝗿𝗿𝟱 != nil {
err = 𝗲𝗿𝗿𝟱
return
}
if 𝘃𝗮𝗹𝟭 == 𝘃𝗮𝗹𝟯 {
{
_, 𝗲𝗿𝗿𝟲 := ret1Err[D]()
if 𝗲𝗿𝗿𝟲 != nil {
err = 𝗲𝗿𝗿𝟲
return
}
}
{
_, 𝗲𝗿𝗿𝟳 := ret1Err[F]()
if 𝗲𝗿𝗿𝟳 != nil {
err = 𝗲𝗿𝗿𝟳
return
}
}
} else {
𝘃𝗮𝗹𝟰, 𝗲𝗿𝗿𝟴 := ret1Err[E]()
if 𝗲𝗿𝗿𝟴 != nil {
err = 𝗲𝗿𝗿𝟴
return
}
if 𝘃𝗮𝗹𝟭 == 𝘃𝗮𝗹𝟰 {
_, 𝗲𝗿𝗿𝟵 := ret1Err[F]()
if 𝗲𝗿𝗿𝟵 != nil {
err = 𝗲𝗿𝗿𝟵
return
}
}
}
}
}
|
Select Stmt
Before
|
After
|
type (
A = int
B = int
C = int
D = int
E = int
F = int
G = int
H = int
)
select {
case <-Try(ret1Err[chan A]()):
case *Try(ret1Err[*B]()), *Try(ret1Err[*bool]()) =
<-Try(ret1Err[chan C]()):
case Try(ret1Err[chan D]())
<- Try(ret1Err[E]()):
case Try(ret1Err[[]F]())[Try(ret1Err[G]())] =
<-Try(ret1Err[chan H]()):
default:
}
|
type (
A = int
B = int
C = int
D = int
E = int
F = int
G = int
H = int
)
𝘃𝗮𝗹𝟭, 𝗲𝗿𝗿𝟭 := ret1Err[chan A]()
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟮 := ret1Err[chan C]()
if 𝗲𝗿𝗿𝟮 != nil {
return 𝗲𝗿𝗿𝟮
}
𝘃𝗮𝗹𝟲, 𝗲𝗿𝗿𝟱 := ret1Err[chan D]()
if 𝗲𝗿𝗿𝟱 != nil {
return 𝗲𝗿𝗿𝟱
}
𝘃𝗮𝗹𝟳, 𝗲𝗿𝗿𝟲 := ret1Err[E]()
if 𝗲𝗿𝗿𝟲 != nil {
return 𝗲𝗿𝗿𝟲
}
𝘃𝗮𝗹𝟴, 𝗲𝗿𝗿𝟳 := ret1Err[chan H]()
if 𝗲𝗿𝗿𝟳 != nil {
return 𝗲𝗿𝗿𝟳
}
select {
case <-𝘃𝗮𝗹𝟭:
case 𝘃𝗮𝗹𝟰, 𝗼𝗸𝟭 := <-𝘃𝗮𝗹𝟮:
𝘃𝗮𝗹𝟯, 𝗲𝗿𝗿𝟯 := ret1Err[*B]()
if 𝗲𝗿𝗿𝟯 != nil {
return 𝗲𝗿𝗿𝟯
}
*𝘃𝗮𝗹𝟯 = 𝘃𝗮𝗹𝟰
𝘃𝗮𝗹𝟱, 𝗲𝗿𝗿𝟰 := ret1Err[*bool]()
if 𝗲𝗿𝗿𝟰 != nil {
return 𝗲𝗿𝗿𝟰
}
*𝘃𝗮𝗹𝟱 = 𝗼𝗸𝟭
case 𝘃𝗮𝗹𝟲 <- 𝘃𝗮𝗹𝟳:
case 𝘃𝗮𝗹𝟭𝟭 := <-𝘃𝗮𝗹𝟴:
𝘃𝗮𝗹𝟵, 𝗲𝗿𝗿𝟴 := ret1Err[[]F]()
if 𝗲𝗿𝗿𝟴 != nil {
return 𝗲𝗿𝗿𝟴
}
𝘃𝗮𝗹𝟭𝟬, 𝗲𝗿𝗿𝟵 := ret1Err[G]()
if 𝗲𝗿𝗿𝟵 != nil {
return 𝗲𝗿𝗿𝟵
}
𝘃𝗮𝗹𝟵[𝘃𝗮𝗹𝟭𝟬] = 𝘃𝗮𝗹𝟭𝟭
default:
}
|
Goto Stmt
Before
|
After
|
L:
var a = Try(ret1Err[int]())
goto L
println(a)
|
L:
𝘃𝗮𝗹𝟭, 𝗲𝗿𝗿𝟭 := ret1Err[int]()
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
var a = 𝘃𝗮𝗹𝟭
goto L
println(a)
|
Assign Stmt
Before
|
After
|
*id(&i) = Try(ret1Err[int]())
|
𝘃𝗮𝗹𝟭 := id(&i)
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟭 := ret1Err[int]()
if 𝗲𝗿𝗿𝟭 != nil {
return
}
*𝘃𝗮𝗹𝟭 = 𝘃𝗮𝗹𝟮
|
Map Index Expr
Before
|
After
|
// panic when writting nil map
{
var m map[int]int
m[Try(ret1Err[int]())] = 1
}
// won't panic when reading nil map
{
var m map[int]int
println(m[0], Try(ret1Err[int]()))
}
// panic when reading map[any]T
{
var m map[any]int
println(m[0], Try(ret1Err[int]()))
}
|
{
var m map[int]int
𝘃𝗮𝗹𝟭, 𝗲𝗿𝗿𝟭 := ret1Err[int]()
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
m[𝘃𝗮𝗹𝟭] = 1
}
{
var m map[int]int
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟮 := ret1Err[int]()
if 𝗲𝗿𝗿𝟮 != nil {
return 𝗲𝗿𝗿𝟮
}
println(m[0], 𝘃𝗮𝗹𝟮)
}
{
var m map[any]int
𝘃𝗮𝗹𝟯 := m[0]
𝘃𝗮𝗹𝟰, 𝗲𝗿𝗿𝟯 := ret1Err[int]()
if 𝗲𝗿𝗿𝟯 != nil {
return 𝗲𝗿𝗿𝟯
}
println(𝘃𝗮𝗹𝟯, 𝘃𝗮𝗹𝟰)
}
|
Type Assert
Before
|
After
|
expr, ok := Try(ret1Err[ast.Node]()).(ast.Expr)
_, _ = expr, ok
|
𝘃𝗮𝗹𝟭, 𝗲𝗿𝗿𝟭 := ret1Err[ast.Node]()
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
expr, ok := 𝘃𝗮𝗹𝟭.(ast.Expr)
_, _ = expr, ok
|
Tuple Assign
Before
|
After
|
_, _ = map[int]int{}[Try(ret1Err[int]())]
|
𝘃𝗮𝗹𝟭, 𝗲𝗿𝗿𝟭 := ret1Err[int]()
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
_, _ = map[int]int{}[𝘃𝗮𝗹𝟭]
|
Selector Expr
Before
|
After
|
func rewrite_ptr_selector_expr() error {
var x *ast.CallExpr
{
// MAY PANIC
consume2(x.Args, Try(ret1Err[string]()))
}
{
// MUST NOT PANIC
consume2(x.Pos, Try(ret1Err[string]()))
}
{
// MAY PANIC
consume2(x.Pos(), Try(ret1Err[string]()))
}
return nil
}
func rewrite_iface_selector_expr() error {
var x ast.Node
{
// MAY PANIC
consume2(x.Pos, Try(ret1Err[string]()))
}
{
// MAY PANIC
consume2(x.Pos(), Try(ret1Err[string]()))
}
return nil
}
|
func rewrite_ptr_selector_expr() error {
var x *ast.CallExpr
{
𝘃𝗮𝗹𝟭 := x.Args
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟭 := ret1Err[string]()
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
consume2(𝘃𝗮𝗹𝟭, 𝘃𝗮𝗹𝟮)
}
{
𝘃𝗮𝗹𝟯, 𝗲𝗿𝗿𝟮 := ret1Err[string]()
if 𝗲𝗿𝗿𝟮 != nil {
return 𝗲𝗿𝗿𝟮
}
consume2(x.Pos, 𝘃𝗮𝗹𝟯)
}
{
𝘃𝗮𝗹𝟰 := x.Pos()
𝘃𝗮𝗹𝟱, 𝗲𝗿𝗿𝟯 := ret1Err[string]()
if 𝗲𝗿𝗿𝟯 != nil {
return 𝗲𝗿𝗿𝟯
}
consume2(𝘃𝗮𝗹𝟰, 𝘃𝗮𝗹𝟱)
}
return nil
}
func rewrite_iface_selector_expr() error {
var x ast.Node
{
𝘃𝗮𝗹𝟭 := x.Pos
𝘃𝗮𝗹𝟮, 𝗲𝗿𝗿𝟭 := ret1Err[string]()
if 𝗲𝗿𝗿𝟭 != nil {
return 𝗲𝗿𝗿𝟭
}
consume2(𝘃𝗮𝗹𝟭, 𝘃𝗮𝗹𝟮)
}
{
𝘃𝗮𝗹𝟯 := x.Pos()
𝘃𝗮𝗹𝟰, 𝗲𝗿𝗿𝟮 := ret1Err[string]()
if 𝗲𝗿𝗿𝟮 != nil {
return 𝗲𝗿𝗿𝟮
}
consume2(𝘃𝗮𝗹𝟯, 𝘃𝗮𝗹𝟰)
}
return nil
}
|
Runtime Panic
Before
|
After
|
type X struct{ x int }
{
var x X
_ = x.x + Try(ret1Err[int]())
}
{
var x *X
_ = x.x + Try(ret1Err[int]())
}
|
type X struct{ x int }
{
var x X
𝘃𝗮𝗹𝟭, 𝗲𝗿𝗿𝟭 := ret1Err[int]()
if 𝗲𝗿𝗿𝟭 != nil {
return
}
_ = x.x + 𝘃𝗮𝗹𝟭
}
{
var x *X
𝘃𝗮𝗹𝟮 := x.x
𝘃𝗮𝗹𝟯, 𝗲𝗿𝗿𝟮 := ret1Err[int]()
if 𝗲𝗿𝗿𝟮 != nil {
return
}
_ = 𝘃𝗮𝗹𝟮 + 𝘃𝗮𝗹𝟯
}
|
runtime_panic_try_test.go
|
runtime_panic_test.go
|