Go語言自定義linter靜態(tài)檢查工具_第1頁
Go語言自定義linter靜態(tài)檢查工具_第2頁
Go語言自定義linter靜態(tài)檢查工具_第3頁
Go語言自定義linter靜態(tài)檢查工具_第4頁
Go語言自定義linter靜態(tài)檢查工具_第5頁
已閱讀5頁,還剩4頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第Go語言自定義linter靜態(tài)檢查工具目錄前言Go語言中的靜態(tài)檢查是如何實現(xiàn)制定linter規(guī)則方式一:標準庫實現(xiàn)customlinter方式二:go/analysis集成到golang-cli

前言

通常我們在業(yè)務項目中會借助使用靜態(tài)代碼檢查工具來保證代碼質量,通過靜態(tài)代碼檢查工具我們可以提前發(fā)現(xiàn)一些問題,比如變量未定義、類型不匹配、變量作用域問題、數(shù)組下標越界、內存泄露等問題,工具會按照自己的規(guī)則進行問題的嚴重等級劃分,給出不同的標識和提示,靜態(tài)代碼檢查助我們盡早的發(fā)現(xiàn)問題,Go語言中常用的靜態(tài)代碼檢查工具有golang-lint、golint,這些工具中已經(jīng)制定好了一些規(guī)則,雖然已經(jīng)可以滿足大多數(shù)場景,但是有些時候我們會遇到針對特殊場景來做一些定制化規(guī)則的需求,所以本文我們一起來學習一下如何自定義linter需求;

Go語言中的靜態(tài)檢查是如何實現(xiàn)

眾所周知Go語言是一門編譯型語言,編譯型語言離不開詞法分析、語法分析、語義分析、優(yōu)化、編譯鏈接幾個階段,學過編譯原理的朋友對下面這個圖應該很熟悉:

編譯器將高級語言翻譯成機器語言,會先對源代碼做詞法分析,詞法分析是將字符序列轉換為Token序列的過程,Token一般分為這幾類:關鍵字、標識符、字面量(包含數(shù)字、字符串)、特殊符號(如加號、等號),生成Token序列后,需要進行語法分析,進一步處理后,生成一棵以表達式為結點的語法樹,這個語法樹就是我們常說的AST,在生成語法樹的過程就可以檢測一些形式上的錯誤,比如括號缺少,語法分析完成后,就需要進行語義分析,在這里檢查編譯期所有能檢查靜態(tài)語義,后面的過程就是中間代碼生成、目標代碼生成與優(yōu)化、鏈接,這里就不詳細描述了,這里主要是想引出抽象語法樹(AST),我們的靜態(tài)代碼檢查工具就是通過分析抽象語法樹(AST)根據(jù)定制的規(guī)則來做的;那么抽象語法樹長什么樣子呢我們可以使用標準庫提供的go/ast、go/parser、go/token包來打印出AST,

查看AST,具體AST長什么樣我們可以看下文的例子;

制定linter規(guī)則

假設我們現(xiàn)在要在我們團隊制定這樣一個代碼規(guī)范,所有函數(shù)的第一個參數(shù)類型必須是Context,不符合該規(guī)范的我們要給出警告;好了,現(xiàn)在規(guī)則已經(jīng)定好了,現(xiàn)在我們就來想辦法實現(xiàn)它;先來一個有問題的示例:

//example.go

packagemain

funcadd(a,bint)int{

returna+b

}

對應AST如下:

*ast.FuncDecl{

8...Name:*ast.Ident{

9....NamePos:3:6

10....Name:"add"

11....Obj:*ast.Object{

12.....Kind:func

13.....Name:"add"http://函數(shù)名

14.....Decl:*(obj@7)

15....}

16...}

17...Type:*ast.FuncType{

18....Func:3:1

19....Params:*ast.FieldList{

20.....Opening:3:9

21.....List:[]*ast.Field(len=1){

22......0:*ast.Field{

23.......Names:[]*ast.Ident(len=2){

24........0:*ast.Ident{

25.........NamePos:3:10

26.........Name:"a"

27.........Obj:*ast.Object{

28..........Kind:var

29..........Name:"a"

30..........Decl:*(obj@22)

31.........}

32........}

33........1:*ast.Ident{

34.........NamePos:3:13

35.........Name:"b"

36.........Obj:*ast.Object{

37..........Kind:var

38..........Name:"b"

39..........Decl:*(obj@22)

40.........}

41........}

42.......}

43.......Type:*ast.Ident{

44........NamePos:3:15

45........Name:"int"http://參數(shù)名

46.......}

47......}

48.....}

49.....Closing:3:18

50....}

51....Results:*ast.FieldList{

52.....Opening:-

53.....List:[]*ast.Field(len=1){

54......0:*ast.Field{

55.......Type:*ast.Ident{

56........NamePos:3:20

57........Name:"int"

58.......}

59......}

60.....}

61.....Closing:-

62....}

63...}

方式一:標準庫實現(xiàn)customlinter

通過上面的AST結構我們可以找到函數(shù)參數(shù)類型具體在哪個結構上,因為我們可以根據(jù)這個結構寫出解析代碼如下:

packagemain

import(

"fmt"

"go/ast"

"go/parser"

"go/token"

"log"

"os"

funcmain(){

v:=visitor{fset:token.NewFileSet()}

for_,filePath:=rangeos.Args[1:]{

iffilePath=="--"{//tobeabletorunthislike"gorunmain.go--input.go"

continue

f,err:=parser.ParseFile(v.fset,filePath,nil,0)

iferr!=nil{

log.Fatalf("Failedtoparsefile%s:%s",filePath,err)

ast.Walk(v,f)

typevisitorstruct{

fset*token.FileSet

func(v*visitor)Visit(nodeast.Node)ast.Visitor{

funcDecl,ok:=node.(*ast.FuncDecl)

if!ok{

returnv

params:=funcDecl.Type.Params.List//getparams

//listisequalofzerothatdon'tneedtochecker.

iflen(params)==0{

returnv

firstParamType,ok:=params[0].Type.(*ast.SelectorExpr)

ifokfirstParamType.Sel.Name=="Context"{

returnv

fmt.Printf("%s:%sfunctionfirstparamsshouldbeContext\n",

v.fset.Position(node.Pos()),funcDecl.Name.Name)

returnv

}

然后執(zhí)行命令如下:

$gorun./main.go--./example.go

./example.go:3:1:addfunctionfirstparamsshouldbeContext

通過輸出我們可以看到,函數(shù)add()第一個參數(shù)必須是Context;這就是一個簡單實現(xiàn),因為AST的結構實在是有點復雜,就不在這里詳細介紹每個結構體了,可以看曹大之前寫的一篇文章:golang

和ast

方式二:go/analysis

看過上面代碼的朋友肯定有點抓狂了,有很多實體存在,要開發(fā)一個linter,我們需要搞懂好多實體,好在go/analysis進行了封裝,go/analysis為linter

提供了統(tǒng)一的接口,它簡化了與IDE,metalinters,代碼Review等工具的集成。如,任何go/analysislinter都可以高效的被go

vet執(zhí)行,下面我們通過代碼方式來介紹go/analysis的優(yōu)勢;

新建一個項目代碼結構如下:

.

├──firstparamcontext

│└──firstparamcontext.go

├──go.mod

├──go.sum

└──testfirstparamcontext

├──example.go

└──main.go

添加檢查模塊代碼,在firstparamcontext.go添加如下代碼:

packagefirstparamcontext

import(

"go/ast"

"/x/tools/go/analysis"

varAnalyzer=analysis.Analyzer{

Name:"firstparamcontext",

Doc:"ChecksthatfunctionsfirstparamtypeisContext",

Run:run,

funcrun(pass*analysis.Pass)(interface{},error){

inspect:=func(nodeast.Node)bool{

funcDecl,ok:=node.(*ast.FuncDecl)

if!ok{

returntrue

params:=funcDecl.Type.Params.List//getparams

//listisequalofzerothatdon'tneedtochecker.

iflen(params)==0{

returntrue

firstParamType,ok:=params[0].Type.(*ast.SelectorExpr)

ifokfirstParamType.Sel.Name=="Context"{

returntrue

pass.Reportf(node.Pos(),"''%s'functionfirstparamsshouldbeContext\n",

funcDecl.Name.Name)

returntrue

for_,f:=rangepass.Files{

ast.Inspect(f,inspect)

returnnil,nil

}

然后添加分析器:

packagemain

import(

"asong.cloud/Golang_Dream/code_demo/custom_linter/firstparamcontext"

"/x/tools/go/analysis/singlechecker"

funcmain(){

singlechecker.Main(firstparamcontext.Analyzer)

}

命令行執(zhí)行如下:

$gorun./main.go--./example.go

/Users/go/src/asong.cloud/Golang_Dream/code_demo/custom_linter/testfirstparamcontext/examp

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論