mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-29 07:00:55 +02:00
tools/linters: Add copied golangci-lint lll
linter code
In this commit, we copy the implementation of the golangci-lint lll linter which we currently use in our CI flow during the `make lint` check. This commit copies the code mostly as is (the only exception being the line which trims white space before checking if a line starts with "import"), and formats it to fit our codebase guidelines. A test is also added so that we can be sure that the following commits which adjust the implementation have the intended results. The custom linter is put into its own module as this is requied by the `golangci-lint custom` when building the custom linter binary which includes the plugin.
This commit is contained in:
44
tools/linters/go.mod
Normal file
44
tools/linters/go.mod
Normal file
@@ -0,0 +1,44 @@
|
||||
module github.com/lightningnetwork/lnd/tools/linters
|
||||
|
||||
go 1.22.6
|
||||
|
||||
require (
|
||||
github.com/golangci/golangci-lint v1.62.2
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/tools v0.27.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/ldez/gomoddirectives v0.2.4 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.12.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
90
tools/linters/go.sum
Normal file
90
tools/linters/go.sum
Normal file
@@ -0,0 +1,90 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/golangci/golangci-lint v1.62.2 h1:b8K5K9PN+rZN1+mKLtsZHz2XXS9aYKzQ9i25x3Qnxxw=
|
||||
github.com/golangci/golangci-lint v1.62.2/go.mod h1:ILWWyeFUrctpHVGMa1dg2xZPKoMUTc5OIMgW7HZr34g=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/ldez/gomoddirectives v0.2.4 h1:j3YjBIjEBbqZ0NKtBNzr8rtMHTOrLPeiwTkfUJZ3alg=
|
||||
github.com/ldez/gomoddirectives v0.2.4/go.mod h1:oWu9i62VcQDYp9EQ0ONTfqLNh+mDLWWDO+SO0qSQw5g=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
|
||||
github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
219
tools/linters/ll.go
Normal file
219
tools/linters/ll.go
Normal file
@@ -0,0 +1,219 @@
|
||||
// The following code is based on code from GolangCI.
|
||||
// Source: https://github.com/golangci-lint/pkg/golinters/lll/lll.go
|
||||
// License: GNU
|
||||
|
||||
package linters
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
"github.com/golangci/golangci-lint/pkg/goanalysis"
|
||||
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
const (
|
||||
linterName = "lll"
|
||||
goCommentDirectivePrefix = "//go:"
|
||||
)
|
||||
|
||||
// New creates a new lll linter from the given settings. It satisfies the
|
||||
// signature required by the golangci-lint linter for plugins.
|
||||
func New(settings *config.LllSettings) *goanalysis.Linter {
|
||||
var (
|
||||
mu sync.Mutex
|
||||
resIssues []goanalysis.Issue
|
||||
)
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (any, error) {
|
||||
issues, err := runLll(pass, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
linterName, "Reports long lines",
|
||||
[]*analysis.Analyzer{analyzer}, nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runLll(pass *analysis.Pass, settings *config.LllSettings) (
|
||||
[]goanalysis.Issue, error) {
|
||||
|
||||
var (
|
||||
fileNames = getFileNames(pass)
|
||||
spaces = strings.Repeat(" ", settings.TabWidth)
|
||||
issues []goanalysis.Issue
|
||||
)
|
||||
|
||||
for _, f := range fileNames {
|
||||
lintIssues, err := getLLLIssuesForFile(
|
||||
f, settings.LineLength, spaces,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range lintIssues {
|
||||
issues = append(issues, goanalysis.NewIssue(
|
||||
&lintIssues[i], pass,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return issues, nil
|
||||
}
|
||||
|
||||
func getLLLIssuesForFile(filename string, maxLineLen int,
|
||||
tabSpaces string) ([]result.Issue, error) {
|
||||
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't open file %s: %w", filename, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var (
|
||||
res []result.Issue
|
||||
lineNumber int
|
||||
multiImportEnabled bool
|
||||
)
|
||||
|
||||
// Scan over each line.
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
lineNumber++
|
||||
|
||||
// Replace all tabs with spaces.
|
||||
line := scanner.Text()
|
||||
line = strings.ReplaceAll(line, "\t", tabSpaces)
|
||||
|
||||
// Ignore any //go: directives since these cant be wrapped onto
|
||||
// a new line.
|
||||
if strings.HasPrefix(line, goCommentDirectivePrefix) {
|
||||
continue
|
||||
}
|
||||
|
||||
// We never want the linter to run on imports since these cannot
|
||||
// be wrapped onto a new line. If this is a single line import
|
||||
// we can skip the line entirely. If this is a multi-line import
|
||||
// skip until the closing bracket.
|
||||
//
|
||||
// NOTE: We trim the line space around the line here purely for
|
||||
// the purpose of being able to test this part of the linter
|
||||
// without the risk of the `gosimports` tool reformatting the
|
||||
// test case and removing the import.
|
||||
if strings.HasPrefix(strings.TrimSpace(line), "import") {
|
||||
multiImportEnabled = strings.HasSuffix(line, "(")
|
||||
continue
|
||||
}
|
||||
|
||||
// If we have marked the start of a multi-line import, we should
|
||||
// skip until the closing bracket of the import block.
|
||||
if multiImportEnabled {
|
||||
if line == ")" {
|
||||
multiImportEnabled = false
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise, we can check the length of the line and report if
|
||||
// it exceeds the maximum line length.
|
||||
lineLen := utf8.RuneCountInString(line)
|
||||
if lineLen > maxLineLen {
|
||||
res = append(res, result.Issue{
|
||||
Pos: token.Position{
|
||||
Filename: filename,
|
||||
Line: lineNumber,
|
||||
},
|
||||
Text: fmt.Sprintf("the line is %d "+
|
||||
"characters long, which exceeds the "+
|
||||
"maximum of %d characters.", lineLen,
|
||||
maxLineLen),
|
||||
FromLinter: linterName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
if errors.Is(err, bufio.ErrTooLong) &&
|
||||
maxLineLen < bufio.MaxScanTokenSize {
|
||||
|
||||
// scanner.Scan() might fail if the line is longer than
|
||||
// bufio.MaxScanTokenSize. In the case where the
|
||||
// specified maxLineLen is smaller than
|
||||
// bufio.MaxScanTokenSize we can return this line as a
|
||||
// long line instead of returning an error. The reason
|
||||
// for this change is that this case might happen with
|
||||
// autogenerated files. The go-bindata tool for instance
|
||||
// might generate a file with a very long line. In this
|
||||
// case, as it's an auto generated file, the warning
|
||||
// returned by lll will be ignored.
|
||||
// But if we return a linter error here, and this error
|
||||
// happens for an autogenerated file the error will be
|
||||
// discarded (fine), but all the subsequent errors for
|
||||
// lll will be discarded for other files, and we'll miss
|
||||
// legit error.
|
||||
res = append(res, result.Issue{
|
||||
Pos: token.Position{
|
||||
Filename: filename,
|
||||
Line: lineNumber,
|
||||
Column: 1,
|
||||
},
|
||||
Text: fmt.Sprintf("line is more than "+
|
||||
"%d characters",
|
||||
bufio.MaxScanTokenSize),
|
||||
FromLinter: linterName,
|
||||
})
|
||||
} else {
|
||||
return nil, fmt.Errorf("can't scan file %s: %w",
|
||||
filename, err)
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func getFileNames(pass *analysis.Pass) []string {
|
||||
var fileNames []string
|
||||
for _, f := range pass.Files {
|
||||
fileName := pass.Fset.PositionFor(f.Pos(), true).Filename
|
||||
ext := filepath.Ext(fileName)
|
||||
if ext != "" && ext != ".go" {
|
||||
// The position has been adjusted to a non-go file,
|
||||
// revert to original file.
|
||||
position := pass.Fset.PositionFor(f.Pos(), false)
|
||||
fileName = position.Filename
|
||||
}
|
||||
fileNames = append(fileNames, fileName)
|
||||
}
|
||||
return fileNames
|
||||
}
|
97
tools/linters/ll_test.go
Normal file
97
tools/linters/ll_test.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package linters
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestGetLLLIssuesForFile tests the line-too-long linter.
|
||||
//
|
||||
//nolint:ll
|
||||
func TestGetLLLIssuesForFile(t *testing.T) {
|
||||
// Test data
|
||||
testCases := []struct {
|
||||
name string
|
||||
content string
|
||||
expectedIssue []string
|
||||
}{
|
||||
{
|
||||
name: "Single long line",
|
||||
content: `
|
||||
fmt.Println("This is a very long line that exceeds the maximum length and should be flagged by the linter.")`,
|
||||
expectedIssue: []string{
|
||||
"the line is 140 characters long, which " +
|
||||
"exceeds the maximum of 80 characters.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple long lines",
|
||||
content: `
|
||||
fmt.Println("This is a very long line that exceeds the maximum length and should be flagged by the linter.")
|
||||
fmt.Println("This is a another very long line that exceeds the maximum length and should be flagged by the linter.")`,
|
||||
expectedIssue: []string{
|
||||
"the line is 140 characters long, which " +
|
||||
"exceeds the maximum of 80 characters.",
|
||||
"the line is 148 characters long, which " +
|
||||
"exceeds the maximum of 80 characters.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Short lines",
|
||||
content: `
|
||||
fmt.Println("Short line")`,
|
||||
},
|
||||
{
|
||||
name: "Directive ignored",
|
||||
content: `//go:generate something very very very very very very very very very long and complex here wowowow`,
|
||||
},
|
||||
{
|
||||
name: "Long single line import",
|
||||
content: `import "github.com/lightningnetwork/lnd/lnrpc/walletrpc/more/more/more/more/more/more/ok/that/is/enough"`,
|
||||
},
|
||||
{
|
||||
name: "Multi-line import",
|
||||
content: `
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc/more/ok/that/is/enough"
|
||||
)`,
|
||||
},
|
||||
{
|
||||
name: "Long single line log",
|
||||
content: `
|
||||
log.Infof("This is a very long log line but since it is a log line, it should be skipped by the linter.")`,
|
||||
expectedIssue: []string{
|
||||
"the line is 121 characters long, which " +
|
||||
"exceeds the maximum of 80 characters.",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tabSpaces := strings.Repeat(" ", 8)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Write content to a temporary file.
|
||||
tmpFile := t.TempDir() + "/test.go"
|
||||
err := os.WriteFile(tmpFile, []byte(tc.content), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Run the linter on the file.
|
||||
issues, err := getLLLIssuesForFile(
|
||||
tmpFile, 80, tabSpaces,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, issues, len(tc.expectedIssue))
|
||||
|
||||
for i, issue := range issues {
|
||||
require.Equal(t, tc.expectedIssue[i], issue.Text)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user