목차
方式一:标准库实现custom linter" >方式一:标准库实现custom linter
方式二:go/analysis" >方式二:go/analysis
集成到golang-cli" >集成到golang-cli
백엔드 개발 Golang Go 언어로 linter(정적 검사 도구)를 사용자 정의하는 방법

Go 언어로 linter(정적 검사 도구)를 사용자 정의하는 방법

Aug 04, 2023 pm 05:32 PM
golang 언어로 가다

머리말

보통 우리는 비즈니스 프로젝트에서 코드 품질을 보장하기 위해 정적 코드 검사 도구를 사용합니다. 정적 코드 검사 도구를 통해 정의되지 않은 변수, 유형 불일치 및 변수 범위와 같은 몇 가지 문제를 미리 찾을 수 있습니다. 문제, 범위를 벗어난 배열 첨자, 메모리 누수 등과 같은 문제가 있는 경우 도구는 자체 규칙에 따라 문제의 심각도를 분류하고 다양한 레이블과 프롬프트를 제공하여 문제를 조기에 발견하는 데 도움이 됩니다. 가능합니다. Go에서 일반적으로 사용되는 정적 코드 검사 도구 언어는<code style="font -size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px; background-color: rgba(27, 31, 35 , 0.05);font-family: " operator mono consolas monaco menlo monospace word-break: break-all rgb>golang-lint, golint, 여기에 몇 가지 규칙이 공식화되었습니다. 도구는 이미 대부분의 시나리오를 충족할 수 있지만 때로는 특별한 시나리오에 대한 일부 사용자 정의 규칙을 만들어야 할 경우가 있으므로 이 문서에서는 Linter 요구 사항을 사용자 정의하는 방법을 알아봅니다. Go语言中常用的静态代码检查工具有golang-lintgolint,这些工具中已经制定好了一些规则,虽然已经可以满足大多数场景,但是有些时候我们会遇到针对特殊场景来做一些定制化规则的需求,所以本文我们一起来学习一下如何自定义linter需求;

Go语言中的静态检查是如何实现?

众所周知Go

🎜🎜Go에서 정적 검사를 구현하는 방법; 언어? 🎜🎜우리 모두는 Go 언어는 컴파일러 컴파일된 언어와 컴파일된 언어는 어휘 분석, 구문 분석, 의미 분석, 최적화, 컴파일 및 연결 단계와 분리될 수 없습니다. 컴파일의 원리를 배운 친구들은 다음 그림을 잘 알고 있어야 합니다. 🎜
Go 언어로 linter(정적 검사 도구)를 사용자 정의하는 방법

컴파일러는 먼저 소스 코드에 대한 어휘 분석을 수행합니다. 어휘 분석은 문자 시퀀스를 Token 시퀀스로 변환하는 과정입니다. : 키워드, 식별자, 리터럴(숫자, 문자열 포함), 특수 기호(예: 더하기 기호, 등호), 생성 토큰시퀀스 후에 구문 분석을 수행해야 합니다. 추가 처리 후 표현식이 노드인 구문 트리가 생성됩니다. 이 구문 트리는 우리가 자주 사용하는 것입니다. AST, 구문 생성 중 tree 구문 분석이 완료된 후 일부 형식 오류를 감지할 수 있으며, 여기서 컴파일 기간 동안 확인할 수 있는 모든 정적 의미를 확인하는 과정은 중간 코드 생성입니다. 여기서는 추상 구문 트리(AST)를 소개하는 것이 주요 목적입니다. 우리의 정적 코드 검사 도구는 추상 구문 트리(AST)를 분석하여 수행합니다. ) 사용자 정의된 규칙에 따라 그러면 추상 구문 트리가 길어집니다. 어떻게 생겼나요? 다음에서 제공하는 go/ast go/parser、 go/token 인쇄할 패키지AST, 시각화 도구를 사용할 수도 있습니다: http ://goast.yuroyoro.net/ 보기AST 코드>, 특정우리는 AST에 대한 아래 예를 볼 수 있습니다. 는 ;Token序列后,需要进行语法分析,进一步处理后,生成一棵以 表达式为结点的 语法树,这个语法树就是我们常说的AST,在生成语法树的过程就可以检测一些形式上的错误,比如括号缺少,语法分析完成后,就需要进行语义分析,在这里检查编译期所有能检查静态语义,后面的过程就是中间代码生成、目标代码生成与优化、链接,这里就不详细描述了,这里主要是想引出抽象语法树(AST),我们的静态代码检查工具就是通过分析抽象语法树(AST)根据定制的规则来做的;那么抽象语法树长什么样子呢?我们可以使用标准库提供的go/astgo/parsergo/token包来打印出AST,也可以使用可视化工具:http://goast.yuroyoro.net/ 查看AST,具体AST처럼 보입니다.

린터 규칙 개발

이제 우리 팀에서 이러한 코드 사양을 공식화한다고 가정해 보겠습니다. 모든 함수의 첫 번째 매개 변수 유형은 Context, 사양을 준수하지 않으면 경고를 표시합니다. 자, 이제 규칙이 설정되었습니다. 방법을 찾으려면 먼저 문제가 있는 예를 들어보세요. Context,不符合该规范的我们要给出警告;好了,现在规则已经定好了,现在我们就来想办法实现它;先来一个有问题的示例:

// example.go
package main

func add(a, b int) int {
 return a + 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" // 函数名
    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" // 参数名
    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  .  .  .  }
로그인 후 복사

方式一:标准库实现custom linter

通过上面的AST结构我们可以找到函数参数类型具体在哪个结构上,因为我们可以根据这个结构写出解析代码如下:

package main

import (
 "fmt"
 "go/ast"
 "go/parser"
 "go/token"
 "log"
 "os"
)

func main() {
 v := visitor{fset: token.NewFileSet()}
 for _, filePath := range os.Args[1:] {
  if filePath == "--" { // to be able to run this like "go run main.go -- input.go"
   continue
  }

  f, err := parser.ParseFile(v.fset, filePath, nil, 0)
  if err != nil {
   log.Fatalf("Failed to parse file %s: %s", filePath, err)
  }
  ast.Walk(&v, f)
 }
}

type visitor struct {
 fset *token.FileSet
}

func (v *visitor) Visit(node ast.Node) ast.Visitor {
 funcDecl, ok := node.(*ast.FuncDecl)
 if !ok {
  return v
 }

 params := funcDecl.Type.Params.List // get params
 // list is equal of zero that don&#39;t need to checker.
 if len(params) == 0 {
  return v
 }

 firstParamType, ok := params[0].Type.(*ast.SelectorExpr)
 if ok && firstParamType.Sel.Name == "Context" {
  return v
 }

 fmt.Printf("%s: %s function first params should be Context\n",
  v.fset.Position(node.Pos()), funcDecl.Name.Name)
 return v
}
로그인 후 복사

然后执行命令如下:

$ go run ./main.go -- ./example.go
./example.go:3:1: add function first params should be Context
로그인 후 복사

通过输出我们可以看到,函数add()第一个参数必须是Context;这就是一个简单实现,因为AST

.
├── firstparamcontext
│   └── firstparamcontext.go
├── go.mod
├── go.sum
└── testfirstparamcontext
    ├── example.go
    └── main.go
로그인 후 복사
로그인 후 복사

AST는 다음과 같습니다. 🎜
package firstparamcontext

import (
 "go/ast"

 "golang.org/x/tools/go/analysis"
)

var Analyzer = &analysis.Analyzer{
 Name: "firstparamcontext",
 Doc:  "Checks that functions first param type is Context",
 Run:  run,
}

func run(pass *analysis.Pass) (interface{}, error) {
 inspect := func(node ast.Node) bool {
  funcDecl, ok := node.(*ast.FuncDecl)
  if !ok {
   return true
  }

  params := funcDecl.Type.Params.List // get params
  // list is equal of zero that don&#39;t need to checker.
  if len(params) == 0 {
   return true
  }

  firstParamType, ok := params[0].Type.(*ast.SelectorExpr)
  if ok && firstParamType.Sel.Name == "Context" {
   return true
  }

  pass.Reportf(node.Pos(), "&#39;&#39;%s&#39; function first params should be Context\n",
   funcDecl.Name.Name)
  return true
 }

 for _, f := range pass.Files {
  ast.Inspect(f, inspect)
 }
 return nil, nil
}
로그인 후 복사
로그인 후 복사
🎜🎜🎜방법 1: 표준 라이브러리는 위의 AST 구조 함수 매개변수 유형이 어떤 구조에 있는지 알 수 있는데, 이 구조를 기반으로 다음과 같이 구문 분석 코드를 작성할 수 있기 때문입니다. : 🎜
package main

import (
 "asong.cloud/Golang_Dream/code_demo/custom_linter/firstparamcontext"
 "golang.org/x/tools/go/analysis/singlechecker"
)

func main() {
 singlechecker.Main(firstparamcontext.Analyzer)
}
로그인 후 복사
로그인 후 복사
🎜 그런 다음 다음과 같이 명령을 실행합니다. 🎜
$ go run ./main.go -- ./example.go 
/Users/go/src/asong.cloud/Golang_Dream/code_demo/custom_linter/testfirstparamcontext/example.go:3:1: &#39;&#39;add&#39; function first params should be Context
로그인 후 복사
로그인 후 복사
🎜 출력을 통해 add()의 첫 번째 매개변수는 Context여야 합니다. 이는 AST의 구조는 실제로 조금 복잡하므로 여기서는 각각에 대해 자세히 소개하지 않겠습니다. 구조에 대해서는 이전에 Cao Da가 쓴 글인 golang 및 ast🎜를 읽어보세요.

方式二:go/analysis

看过上面代码的朋友肯定有点抓狂了,有很多实体存在,要开发一个linter,我们需要搞懂好多实体,好在go/analysis进行了封装,go/analysislinter 提供了统一的接口,它简化了与IDE,metalinters,代码Review等工具的集成。如,任何go/analysislinter都可以高效的被go vet执行,下面我们通过代码方式来介绍go/analysis的优势;

新建一个项目代码结构如下:

.
├── firstparamcontext
│   └── firstparamcontext.go
├── go.mod
├── go.sum
└── testfirstparamcontext
    ├── example.go
    └── main.go
로그인 후 복사
로그인 후 복사

添加检查模块代码,在firstparamcontext.go添加如下代码:

package firstparamcontext

import (
 "go/ast"

 "golang.org/x/tools/go/analysis"
)

var Analyzer = &analysis.Analyzer{
 Name: "firstparamcontext",
 Doc:  "Checks that functions first param type is Context",
 Run:  run,
}

func run(pass *analysis.Pass) (interface{}, error) {
 inspect := func(node ast.Node) bool {
  funcDecl, ok := node.(*ast.FuncDecl)
  if !ok {
   return true
  }

  params := funcDecl.Type.Params.List // get params
  // list is equal of zero that don&#39;t need to checker.
  if len(params) == 0 {
   return true
  }

  firstParamType, ok := params[0].Type.(*ast.SelectorExpr)
  if ok && firstParamType.Sel.Name == "Context" {
   return true
  }

  pass.Reportf(node.Pos(), "&#39;&#39;%s&#39; function first params should be Context\n",
   funcDecl.Name.Name)
  return true
 }

 for _, f := range pass.Files {
  ast.Inspect(f, inspect)
 }
 return nil, nil
}
로그인 후 복사
로그인 후 복사

然后添加分析器:

package main

import (
 "asong.cloud/Golang_Dream/code_demo/custom_linter/firstparamcontext"
 "golang.org/x/tools/go/analysis/singlechecker"
)

func main() {
 singlechecker.Main(firstparamcontext.Analyzer)
}
로그인 후 복사
로그인 후 복사

命令行执行如下:

$ go run ./main.go -- ./example.go 
/Users/go/src/asong.cloud/Golang_Dream/code_demo/custom_linter/testfirstparamcontext/example.go:3:1: &#39;&#39;add&#39; function first params should be Context
로그인 후 복사
로그인 후 복사

如果我们想添加更多的规则,使用golang.org/x/tools/go/analysis/multichecker追加即可。

集成到golang-cli

我们可以把golang-cli的代码下载到本地,然后在pkg/golinters 下添加firstparamcontext.go,代码如下:

import (
 "golang.org/x/tools/go/analysis"

 "github.com/golangci/golangci-lint/pkg/golinters/goanalysis"

 "github.com/fisrtparamcontext"
)


func NewfirstparamcontextCheck() *goanalysis.Linter {
 return goanalysis.NewLinter(
  "firstparamcontext",
  "Checks that functions first param type is Context",
  []*analysis.Analyzer{firstparamcontext.Analyzer},
  nil,
 ).WithLoadMode(goanalysis.LoadModeSyntax)
}
로그인 후 복사

然后重新make一个golang-cli可执行文件,加到我们的项目中就可以了;

总结

golang-cli仓库中pkg/golinters目录下存放了很多静态检查代码,学会一个知识点的最快办法就是抄代码,先学会怎么使用的,慢慢再把它变成我们自己的;本文没有对AST标准库做过多的介绍,因为这部分文字描述比较难以理解,最好的办法还是自己去看官方文档、加上实践才能更快的理解。

本文所有代码已经上传:https://github.com/asong2020/Golang_Dream/tree/master/code_demo/custom_linter


위 내용은 Go 언어로 linter(정적 검사 도구)를 사용자 정의하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Golang을 사용하여 파일을 안전하게 읽고 쓰는 방법은 무엇입니까? Golang을 사용하여 파일을 안전하게 읽고 쓰는 방법은 무엇입니까? Jun 06, 2024 pm 05:14 PM

Go에서는 안전하게 파일을 읽고 쓰는 것이 중요합니다. 지침은 다음과 같습니다. 파일 권한 확인 지연을 사용하여 파일 닫기 파일 경로 유효성 검사 컨텍스트 시간 초과 사용 다음 지침을 따르면 데이터 보안과 애플리케이션의 견고성이 보장됩니다.

Golang 데이터베이스 연결을 위한 연결 풀을 구성하는 방법은 무엇입니까? Golang 데이터베이스 연결을 위한 연결 풀을 구성하는 방법은 무엇입니까? Jun 06, 2024 am 11:21 AM

Go 데이터베이스 연결을 위한 연결 풀링을 구성하는 방법은 무엇입니까? 데이터베이스 연결을 생성하려면 데이터베이스/sql 패키지의 DB 유형을 사용하고, 최대 동시 연결 수를 제어하려면 MaxIdleConns를 설정하고, 연결의 최대 수명 주기를 제어하려면 ConnMaxLifetime을 설정하세요.

Golang과 C++의 유사점과 차이점 Golang과 C++의 유사점과 차이점 Jun 05, 2024 pm 06:12 PM

Golang과 C++는 각각 가비지 수집 및 수동 메모리 관리 프로그래밍 언어로, 구문과 유형 시스템이 다릅니다. Golang은 Goroutine을 통해 동시 프로그래밍을 구현하고, C++는 스레드를 통해 이를 구현합니다. Golang 메모리 관리는 간단하고 C++는 더 강력한 성능을 제공합니다. 실제적인 경우 Golang 코드는 더 간결하며 C++는 확실한 성능 이점을 제공합니다.

golang 프레임워크 아키텍처의 학습 곡선은 얼마나 가파르나요? golang 프레임워크 아키텍처의 학습 곡선은 얼마나 가파르나요? Jun 05, 2024 pm 06:59 PM

Go 프레임워크 아키텍처의 학습 곡선은 Go 언어 및 백엔드 개발에 대한 친숙도와 선택한 프레임워크의 복잡성, 즉 Go 언어의 기본 사항에 대한 올바른 이해에 따라 달라집니다. 백엔드 개발 경험이 있으면 도움이 됩니다. 다양한 복잡성의 프레임워크는 다양한 학습 곡선으로 이어집니다.

Golang의 목록에서 임의의 요소를 생성하는 방법은 무엇입니까? Golang의 목록에서 임의의 요소를 생성하는 방법은 무엇입니까? Jun 05, 2024 pm 04:28 PM

Golang에서 목록의 임의 요소를 생성하는 방법: rand.Intn(len(list))을 사용하여 목록의 길이 범위 내에서 임의의 정수를 생성합니다. 정수를 인덱스로 사용하여 목록에서 해당 요소를 가져옵니다.

golang 프레임워크의 장점과 단점 비교 golang 프레임워크의 장점과 단점 비교 Jun 05, 2024 pm 09:32 PM

Go 프레임워크는 높은 성능과 동시성 장점으로 인해 두각을 나타냅니다. 그러나 상대적으로 새로운 프레임워크, 작은 개발자 생태계, 일부 기능 부족 등 몇 가지 단점도 있습니다. 또한 빠른 변화와 학습 곡선은 프레임워크마다 다를 수 있습니다. Gin 프레임워크는 효율적인 라우팅, 내장된 JSON 지원 및 강력한 오류 처리로 인해 RESTful API를 구축하는 데 널리 사용됩니다.

Golang 프레임워크의 오류 처리에 대한 모범 사례는 무엇입니까? Golang 프레임워크의 오류 처리에 대한 모범 사례는 무엇입니까? Jun 05, 2024 pm 10:39 PM

모범 사례: 잘 정의된 오류 유형(오류 패키지)을 사용하여 사용자 정의 오류 생성 자세한 내용 제공 오류를 적절하게 기록 오류를 올바르게 전파하고 컨텍스트를 추가하기 위해 필요에 따라 오류를 숨기거나 억제하지 않음

golang 프레임워크의 장점은 무엇인가요? golang 프레임워크의 장점은 무엇인가요? Jun 06, 2024 am 10:26 AM

Golang 프레임워크의 장점 Golang은 마이크로서비스 및 분산 시스템에 특히 적합한 고성능 동시 프로그래밍 언어입니다. Golang 프레임워크는 미리 만들어진 구성 요소 및 도구 세트를 제공하여 이러한 애플리케이션을 더 쉽게 개발할 수 있도록 해줍니다. 다음은 Golang 프레임워크의 주요 장점 중 일부입니다. 1. 고성능 및 동시성: Golang 자체는 고성능 및 동시성으로 유명합니다. 코드 동시 실행을 허용하는 경량 스레딩 메커니즘인 고루틴을 사용하여 애플리케이션 처리량과 응답성을 향상시킵니다. 2. 모듈성 및 재사용성: Golang 프레임워크는 모듈성과 재사용 가능한 코드를 장려합니다. 애플리케이션을 독립된 모듈로 분리하면 코드를 쉽게 유지 관리하고 업데이트할 수 있습니다.

See all articles