7

关于Go的空指针检查

 1 year ago
source link: https://zhanggq.github.io/post/zgq-go-nil-pointer/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

最近开发过程中遇到挺多空指针引发的Coredump,想在CI过程中集成一下Go的代码检查工具,但实际发现这些工具的效果都不行……

一、测试代码

简单写了一些空指针代码,如下所示:

package main
import (
"fmt"
"reflect"
"testGolint/context" // 加载这个库时,就会调用其init(),所以其内部打印在main()之前
"time"
type Address struct {
Home string
Email string
type User struct {
Name string
Address *Address
func testCase0() {
fmt.Println("Test Case 0 Begin..")
var a = User{}
address := a.Address
if address == nil {
fmt.Println(*address) // nil dereference
fmt.Printf("Email Address = [%s] \n", address.Email) // 典型的空指针,查不出来
fmt.Println("Test Case 0 Finish..")
func testCase1() {
fmt.Println("Test Case 1 Begin..")
var a = &context.Context{}
var user = a.UserAllocate()
fmt.Printf("User Name %s \n", user.Name)
var company = user.WorkExps[0]
fmt.Printf("User Company Name %s \n", company.CompanyName) // 典型的空指针,查不出来
fmt.Println("Test Case 1 Finish..")
func testCase2() {
fmt.Println("Test Case 2 Begin..")
var a *context.Context
go func() {
fmt.Println("Before a.")
a = &context.Context{}
fmt.Println("After a")
//time.Sleep(1 * time.Second) // 加上这句就不会dump
go func() {
fmt.Println("Before UserInfo.")
a.UserInfo() // 这行很可能运行在a = &context.Context{}之前,会触发空指针dump
fmt.Println("After UserInfo.")
fmt.Println("Test Case 2 Finish..")
time.Sleep(3 * time.Second)
func IsNil(i interface{}) bool {
vi := reflect.ValueOf(i)
if vi.Kind() == reflect.Ptr {
return vi.IsNil()
return false
type Worker interface {
String() string
type Person struct {
Age uint8
Name string
Sex string
type Employee struct {
Person
type Employer struct {
Title string
Person
func (p *Person) String() string {
return fmt.Sprintf("Name: %s, Sex: %s, Age: %d", p.Name, p.Sex, p.Age)
func (er *Employer) String() string {
return fmt.Sprintf("%s, Title: %s", er.Person.String(), er.Title)
func testCase3() {
w Worker
emptyPoint *Employee
w = &Employer{
Title: "Title",
Person: Person{
Name: "Tom",
Age: 50,
Sex: "male",
if IsNil(w) {
fmt.Printf("interface w is nil")
return
// **bad** 是类型断言问题
e := w.(*Employee)
fmt.Println(e.String())
if e, ok := w.(*Employee); ok {
fmt.Printf("It's Employee, %s\n", e.String())
} else if e, ok := w.(*Employer); ok {
fmt.Printf("It's Employer, %s\n", e.String())
// **bad** 在嵌入式结构上定义方法时在nil指针上调用方法
fmt.Println(emptyPoint.String())
func main() {
fmt.Println("Test Begin..")
testCase0()
testCase1()
testCase2()
testCase3()
fmt.Println("Test Finish..")

二、Golangci-lint

名气最大的一个工具Golangci-lint。修改.golangci.yml,放开所有的限制,运行后发现核心的问题没查出来,只有一些格式问题:

golangci-lint.png

三、Nilness

Nilness是官方的一个专门检查空指针的工具,但运行后发现只能查几种简单的空指针场景:

nilness.png

四、Staticcheck

Staticcheck,依然不行:

staticcheck.png

五、Gocritic

Gocritic,不行:

gocritic1.png

gocritic2.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK