当前位置: 首页 > 工具软件 > GolangCI-Lint > 使用案例 >

golangci-lint 自定义linter核心代码 用于检查错误使用gtime.Time.Format() 例如gtime.Time.Format(“2006-01-02 15:04:05“)

范霄
2023-12-01

gtime.Time对象通过Format方法来实现自定义格式的时间日期转换,该方法与标准库time.Time的Format方法冲突。在gtime.Time对象中,通过Layout方法实现标准库time.Time的Format格式,例如:t.Layout(2006-01-02 15:04:05)。

gtime.Time是GoFrame框架的时间类型,它的Format方法的形参与标准库Format方法的形参完全不同。
很多人会使用错误,而且使用错误不会报错;导致难以发现。
本代码只是Demo阶段,未测试,不能保证识别的准确和完整性
文末有示例代码,用于识别测试
本代码参考:https://disaev.me/p/writing-useful-go-analysis-linter/

package analyzer

import (
	"flag"
	"go/ast"
	"golang.org/x/tools/go/analysis"
	"strings"
)

var Analyzer = &analysis.Analyzer{
	Name:  "gtimeFormatLinter",
	Doc:   "gtime.Time is the time type of the Go Frame framework. The formal parameters of its Format method are completely different from those of the Format method in the standard library.",
	Flags: flag.FlagSet{},
	Run:   run,
}

func callMultiplexing(call *ast.CallExpr, pass *analysis.Pass, formatSymbols []string) {
	if call.Args == nil || len(call.Args) == 0 {
		return
	}
	for _, arg := range call.Args {
		if arg == nil {
			continue
		}
		bas, ok := arg.(*ast.BasicLit)
		if !ok {
			continue
		}
		var existence bool
		for _, formatSymbol := range formatSymbols {
			if strings.Contains(bas.Value, formatSymbol) {
				existence = true
			}
		}
		if existence {
			pass.Reportf(bas.Pos(), "Incorrect formal parameters of the Format method of gtime.Time")
		}
	}
}
func run(pass *analysis.Pass) (interface{}, error) {
	var formatSymbols = []string{"2006", "01", "02", "15", "04", "05"}
	inspect := func(node ast.Node) bool {
		fun, ok := node.(*ast.FuncDecl)
		if !ok {
			return true
		}
		if fun.Body == nil || fun.Body.List == nil || len(fun.Body.List) == 0 {
			return true
		}
		for _, stmt := range fun.Body.List {
			if stmt == nil {
				continue
			}
			expr, ok := stmt.(*ast.ExprStmt)
			if !ok {
				continue
			}
			if expr.X == nil {
				continue
			}
			call, ok := expr.X.(*ast.CallExpr)
			if !ok {
				continue
			}
			if call.Fun == nil {
				continue
			}
			sel, ok := call.Fun.(*ast.SelectorExpr)
			if !ok {
				continue
			}
			if sel.Sel == nil || sel.Sel.Name != "Format" {
				continue
			}
			if sel.X == nil {
				continue
			}
			switch sel.X.(type) {
			case *ast.Ident:
				id := sel.X.(*ast.Ident)
				if id.Obj == nil {
					continue
				}
				if id.Obj.Decl == nil {
					continue
				}
				obj, ok := id.Obj.Decl.(*ast.ValueSpec)
				if !ok {
					continue
				}
				if obj.Type == nil {
					continue
				}
				sel2, ok := obj.Type.(*ast.SelectorExpr)
				if !ok {
					continue
				}
				if sel2.X == nil {
					continue
				}
				id2, ok := sel2.X.(*ast.Ident)
				if !ok {
					continue
				}
				if id2.Name != "gtime" {
					continue
				}
				if sel2.Sel == nil {
					continue
				}
				if sel2.Sel.Name != "Time" {
					continue
				}
				callMultiplexing(call, pass, formatSymbols)
			case *ast.CallExpr:
				call2, ok := sel.X.(*ast.CallExpr)
				if !ok {
					continue
				}
				if call2.Fun == nil {
					continue
				}
				sel2, ok := call2.Fun.(*ast.SelectorExpr)
				if !ok {
					continue
				}
				if sel2.X == nil {
					continue
				}
				id, ok := sel2.X.(*ast.Ident)
				if !ok {
					continue
				}
				if id.Name != "gtime" {
					continue
				}
				callMultiplexing(call, pass, formatSymbols)
			}
		}
		return true
	}

	for _, file := range pass.Files {
		ast.Inspect(file, inspect)
	}
	return nil, nil
}

package main

import (
	"github.com/gogf/gf/os/gtime"
	"time"
)

func A() {
	var t gtime.Time
	t.Format("2006-01-02 15:04:05")
	gtime.New().Format("2006-01-02 15:04:05")

	gtime.NewFromStr("2006-01-02 15:04:05").Format("2006-01-02 15:04:05")

	t.Format("%Y-m-d")
	gtime.New().Format("%Y-m-d")

	var sy time.Time
	sy.Format("2006-01-02 15:04:05")
	time.Now().Format("2006-01-02 15:04:05")
}

 类似资料: