5분 안에 맞춤형 Go linter 구축
7604 단어 go
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 }
. . . }
. . }
. }
}
위반 노드가 충족하는 조건은 다음과 같습니다.
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
입니다.이것을
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
Reference
이 문제에 관하여(5분 안에 맞춤형 Go linter 구축), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/geoffreycopin/build-a-custom-go-linter-in-5-minutes-mh9텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)