Golangci-lint

A journey to create a linter

Manuel Doncel Martos -

Powered by reveal.js

QR Code Slides

About me


            var Me = Developer{
                Name: "Manuel Doncel Martos",
                Skills: [][]string{
                  {"Java", "Spring Boot"},
                  {"Go", "Python"},
                  {"Kubernetes", "Docker"},
                },
                Interests: []string {
                  "Open Source", "Domain Driven Design", "Football",
                },
            }
            
QR Code GitHub manuelarte

Agenda

What is golangci-lint

Golangci-lint is a fast linters runner for Go.

  • Widely used.
  • More than 100 linters.

                        @startuml
                        skinparam backgroundColor transparent
                        skinparam monochrome reverse
                        !option handwritten true

                        revive - [golangci-lint]
                        gofmt - [golangci-lint]
                        gofumpt - [golangci-lint]
                        gci - [golangci-lint]
                        ... - [golangci-lint]
                        funcorder -- [golangci-lint]
                        embeddedstructfieldcheck -- [golangci-lint]

                        @enduml
                        

Example configurations


            version: 2
            

Others:

AST Parser


              @startwbs

              skinparam backgroundColor transparent
              skinparam monochrome reverse
              !option handwritten true

              * ast.File
              ** ast.GenDecl
              *** ast.ImportSpec
              **** ast.BasicLit
              ** ast.GenDecl
              *** ast.ValueSpec
              **** ast.Ident
              **** ast.BasicLit
              ** ast.GenDecl
              *** ast.TypeSpec
              *** ast.StructType
              **** ast.FieldList
              ***** ast.Field
              ***** ast.Field
              ** ast.FuncDecl
              *** ast.FuncType
              *** ast.FieldList
              **** ast.Field
              **** ast.Field
              ** ...

              @endwbs
              

File to test: here

Tools Analyzer

Package golang.org/x/tools

  • Analyzer
  • Pass
  • Diagnosis

Create your first linter

Prefix unexported globals with '_' .

❌ Bad

                const myConstant = "myConstant"
                const errNotFound = "not found"
              
✅ Good

                const _myConstant = "myConstant"
                const errNotFound = "not found"
              
  • Identify
  • Suggest fix

🧐 My linters

FuncOrder

  • Constructors after struct declaration.
  • Exported methods before unexported.

EmbeddedStructFieldCheck

  • Embedded fields declared before normal fields.
  • Space between the embedded fields and normal fields.
  • sync.Mutex and sync.RMutex are not allowed as embedded fields.

FuncOrder

❌ Bad

                        // ❌ constructor before
                        // struct declaration
                        func NewMyStruct() *MyStruct {
                            return &MyStruct{}
                        }

                        type MyStruct struct {
                            Name string
                        }

                        // ❌ unexported method
                        // placed before exported method
                        func (m MyStruct) lenName() int {
                            return len(m.Name)
                        }

                        func (m MyStruct) GetName() string {
                            return m.Name
                        }

                        ...
                        type Client struct {
                          version int
                          http.Client
                        }
                 
✅ Good

                        type MyStruct struct {
                            Name string
                        }

                        // ✅ constructor after
                        // struct declaration and
                        // before methods
                        func NewMyStruct() *MyStruct {
                            return &MyStruct{}
                        }

                        // ✅ exported methods before
                        // unexported methods
                        func (m MyStruct) GetName() string {
                            return m.Name
                        }

                        func (m MyStruct) lenName() int {
                            return len(m.Name)
                        }

                        ...
                  

EmbeddedStructFieldCheck

❌ Bad

                        type Client struct {
                          version int
                          http.Client
                        }
                 
✅ Good

                        type Client struct {
                          http.Client

                          version int
                        }
                  

Create custom plugin

More info: Module Plugin

Example here: Custom Plugin

Q/A