5분 안에 맞춤형 Go linter 구축

7604 단어 go
맞춤형 린터를 만드는 것은 코딩 표준을 적용하고 코드 냄새를 감지하는 좋은 방법이 될 수 있습니다. 이 자습서에서는 소스 코드 쿼리 엔진인 Sylver를 사용하여 단 몇 줄의 코드로 사용자 지정 Golang linter를 빌드합니다.

Sylver의 기본 인터페이스는 REPL 콘솔이며, 여기에서 프로젝트의 소스 코드를 로드하여 SYLQ이라는 SQL과 유사한 쿼리 언어를 사용하여 쿼리할 수 있습니다. Linting 규칙을 표현하는 SYLQ개의 쿼리를 작성하면 이를 기존 linter처럼 실행할 수 있는 규칙 세트에 저장할 수 있습니다.

설치


sylver --version이 버전 번호 >= 0.1.8을 출력하지 않으면 https://sylver.dev으로 이동하여 소프트웨어의 새 사본을 다운로드하십시오.

REPL 시작



REPL을 시작하는 것은 프로젝트의 루트에서 다음 명령을 호출하는 것만큼 간단합니다.

sylver query --files="**/*.go" --spec=https://github.com/sylver-dev/golang.git#golang.yaml


REPL은 Ctrl+C을 누르거나 프롬프트에서 :quit을 입력하여 종료할 수 있습니다.

이제 쿼리 코드 다음에 SYLQ 을 입력하여 ; 쿼리를 실행할 수 있습니다.
예를 들어: 모든 구조체 선언을 검색하려면:

match StructType;


쿼리 결과는 다음과 같이 형식화됩니다.

[...]
$359 [StructType association.go:323:17-327:1]
$360 [StructType schema/index.go:10:12-18:1]
$361 [StructType schema/index.go:20:18-27:1]
$362 [StructType tests/group_by_test.go:70:12-73:2]
$363 [StructType schema/check.go:11:12-15:1]


지정된 구조체 선언의 코드는 :print 뒤에 노드 별칭을 입력하여 표시할 수 있습니다(예: :print $362 ). 구문 분석 트리는 :print_ast 명령(예: :print_ast $362 )을 사용하여 표시할 수 있습니다.

규칙 1: 필드가 너무 많은 구조체 선언 감지



첫 번째 규칙으로 필드가 10개 이상인 구조체 선언에 플래그를 지정하려고 합니다.
첫 번째 단계는 구조체 선언의 트리 구조에 익숙해지는 것이므로 ast와 함께 StructType을 인쇄해 보겠습니다.

λ> :print $362

struct {
        Name  string
        Total int64
    }

λ> :print_ast $362

StructType {
. ● fields: List<FieldSpec> {
. . FieldSpec {
. . . ● names: List<Identifier> {
. . . . Identifier { Name }
. . . }
. . . ● type: TypeIdent {
. . . . ● name: Identifier { string }
. . . }
. . }
. . FieldSpec {
. . . ● names: List<Identifier> {
. . . . Identifier { Total }
. . . }
. . . ● type: TypeIdent {
. . . . ● name: Identifier { int64 }
. . . }
. . }
. }
}


구조체의 필드는 fields개의 노드 목록을 보유하는 FieldSpec이라는 적절한 이름의 필드에 저장됩니다. 즉, 규칙을 위반하는 노드는 StructType 목록의 길이가 10보다 긴 모든 fields 노드입니다.
이것은 SYLQ으로 쉽게 표현할 수 있습니다.

 match StructType s when s.fields.length > 10;


규칙 2: 대입 연산자 사용 제안



두 번째 linting 규칙의 경우 다음과 같은 할당 연산자(예: += )를 사용하여 단순화할 수 있는 할당을 식별하고 싶습니다.

x = x + 1


간단한 할당의 구문 분석 트리를 살펴보겠습니다.

λ> :print $5750

err = nil

λ> :print_ast $5750

AssignStmt {
. ● lhs: List<Expr> {
. . Identifier { err }
. }
. ● rhs: List<Expr> {
. . NilLit { nil }
. }
}


따라서 AssignStmt 필드에 왼쪽 피연산자로 rhs이 있는 Binop이 포함된 lhs 노드를 검색하려고 합니다. 또한 할당의 왼쪽에는 단일 표현식이 포함되어야 합니다. 이것은 다음과 같이 작성할 수 있습니다.

match AssignStmt a when
      a.lhs.length == 1
   && a.rhs[0] is { BinOp b when b.left.text == a.lhs[0].text };


규칙 3: make 내장 함수의 잘못된 사용



마지막 linting 규칙의 경우 길이가 용량보다 큰 make 함수의 잘못된 사용을 식별하려고 합니다. 이는 아마도 프로그래밍 오류를 나타낼 수 있기 때문입니다.

다음은 make 호출의 구문 분석 트리입니다.

λ> :print $16991

make([]string, 0, len(value))

λ> :print_ast $16991

CallExpr {
. ● fun: Identifier { make }
. ● args: List<GoNode> {
. . SliceType {
. . . ● elemsType: TypeIdent {
. . . . ● name: Identifier { string }
. . . }
. . }
. . IntLit { 0 }
. . CallExpr {
. . . ● fun: Identifier { len }
. . . ● args: List<GoNode> {
. . . . Identifier { value }
. . . }
. . }
. }
}



위반 노드가 충족하는 조건은 다음과 같습니다.
  • fun의 테스트는 make입니다.
  • 인수 목록에는 3개의 요소가 포함되어 있습니다.
  • 마지막 두 인수는 int 리터럴입니다.
  • 세 번째 인수(용량)가 두 번째 인수(길이)보다 작습니다.

  • 이것을 SYLQ으로 인코딩해 보겠습니다.

    match CallExpr c when
          c.fun.text == 'make'
       && c.args.length == 3
       && c.args[1] is IntLit
       && c.args[2] is IntLit
       && c.args[2].text.to_int() < c.args[1].text.to_int();
    


    규칙 세트 만들기



    다음 규칙 세트는 Linting 규칙을 사용합니다.

    id: customLinter
    
    language: "https://github.com/sylver-dev/golang.git#golang.yaml"
    
    rules:
        - id: largeStruct
          message: struct has many fields
          severity: info
    
          query:  match StructType s when s.fields.length > 10
    
    
        - id: assignOp
          message: assignment should use an assignment operator
          severity: warning
          note: According to our style guide, assignment operators should be preferred.
    
          query: >
            match AssignStmt a when
                 a.lhs.length == 1
              && a.rhs[0] is { BinOp b when b.left.text == a.lhs[0].text }
    
        - id: makeCapacityErr 
          message: capacity should be higher than length     
          severity: error
    
          query: >
            match CallExpr c when
                  c.fun.text == 'make'
              && c.args.length == 3
              && c.args[1] is IntLit
              && c.args[2] is IntLit
              && c.args[2].text.to_int() < c.args[1].text.to_int()
    


    프로젝트의 루트에 있는 custom_linter.yaml이라는 파일에 저장되어 있다고 가정하면 다음 명령으로 실행할 수 있습니다.

    sylver ruleset run --files="**/*.go" --rulesets=custom_linter.yaml
    

    좋은 웹페이지 즐겨찾기