Add go-header linter (#1181)
* add go-header linter * apply review comments: add goheader example into .golangci.example.yml * apply review comments: correctly handle multiline comments
This commit is contained in:
parent
b22e3f1874
commit
01b566a646
@ -157,6 +157,33 @@ linters-settings:
|
|||||||
gofmt:
|
gofmt:
|
||||||
# simplify code: gofmt with `-s` option, true by default
|
# simplify code: gofmt with `-s` option, true by default
|
||||||
simplify: true
|
simplify: true
|
||||||
|
goheader:
|
||||||
|
values:
|
||||||
|
const:
|
||||||
|
# define here const type values in format k:v, for example:
|
||||||
|
# YEAR: 2020
|
||||||
|
# COMPANY: MY COMPANY
|
||||||
|
regexp:
|
||||||
|
# define here regexp type values, for example
|
||||||
|
# AUTHOR: .*@mycompany\.com
|
||||||
|
template:
|
||||||
|
# put here copyright header template for source code files, for example:
|
||||||
|
# {{ AUTHOR }} {{ COMPANY }} {{ YEAR }}
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at:
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
template-path:
|
||||||
|
# also as alternative of directive 'template' you may put the path to file with the template source
|
||||||
goimports:
|
goimports:
|
||||||
# put imports beginning with prefix after 3rd-party packages;
|
# put imports beginning with prefix after 3rd-party packages;
|
||||||
# it's a comma-separated list of prefixes
|
# it's a comma-separated list of prefixes
|
||||||
|
1
go.mod
1
go.mod
@ -6,6 +6,7 @@ require (
|
|||||||
github.com/Djarvur/go-err113 v0.0.0-20200511133814-5174e21577d5
|
github.com/Djarvur/go-err113 v0.0.0-20200511133814-5174e21577d5
|
||||||
github.com/OpenPeeDeeP/depguard v1.0.1
|
github.com/OpenPeeDeeP/depguard v1.0.1
|
||||||
github.com/bombsimon/wsl/v3 v3.1.0
|
github.com/bombsimon/wsl/v3 v3.1.0
|
||||||
|
github.com/denis-tingajkin/go-header v0.3.1
|
||||||
github.com/fatih/color v1.9.0
|
github.com/fatih/color v1.9.0
|
||||||
github.com/go-critic/go-critic v0.4.3
|
github.com/go-critic/go-critic v0.4.3
|
||||||
github.com/go-lintpack/lintpack v0.5.2
|
github.com/go-lintpack/lintpack v0.5.2
|
||||||
|
2
go.sum
2
go.sum
@ -49,6 +49,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/denis-tingajkin/go-header v0.3.1 h1:ymEpSiFjeItCy1FOP+x0M2KdCELdEAHUsNa8F+hHc6w=
|
||||||
|
github.com/denis-tingajkin/go-header v0.3.1/go.mod h1:sq/2IxMhaZX+RRcgHfCRx/m0M5na0fBt4/CRe7Lrji0=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
|
@ -239,6 +239,7 @@ type LintersSettings struct {
|
|||||||
Dogsled DogsledSettings
|
Dogsled DogsledSettings
|
||||||
Gocognit GocognitSettings
|
Gocognit GocognitSettings
|
||||||
Godot GodotSettings
|
Godot GodotSettings
|
||||||
|
Goheader GoHeaderSettings
|
||||||
Testpackage TestpackageSettings
|
Testpackage TestpackageSettings
|
||||||
Nestif NestifSettings
|
Nestif NestifSettings
|
||||||
NoLintLint NoLintLintSettings
|
NoLintLint NoLintLintSettings
|
||||||
@ -247,6 +248,12 @@ type LintersSettings struct {
|
|||||||
Custom map[string]CustomLinterSettings
|
Custom map[string]CustomLinterSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GoHeaderSettings struct {
|
||||||
|
Values map[string]map[string]string `mapstructure:"values"`
|
||||||
|
Template string `mapstructure:"template"`
|
||||||
|
TemplatePath string `mapstructure:"template-path"`
|
||||||
|
}
|
||||||
|
|
||||||
type GovetSettings struct {
|
type GovetSettings struct {
|
||||||
CheckShadowing bool `mapstructure:"check-shadowing"`
|
CheckShadowing bool `mapstructure:"check-shadowing"`
|
||||||
Settings map[string]map[string]interface{}
|
Settings map[string]map[string]interface{}
|
||||||
|
78
pkg/golinters/goheader.go
Normal file
78
pkg/golinters/goheader.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package golinters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/token"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
goheader "github.com/denis-tingajkin/go-header"
|
||||||
|
"golang.org/x/tools/go/analysis"
|
||||||
|
|
||||||
|
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
|
||||||
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
const goHeaderName = "goheader"
|
||||||
|
|
||||||
|
func NewGoHeader() *goanalysis.Linter {
|
||||||
|
var mu sync.Mutex
|
||||||
|
var issues []goanalysis.Issue
|
||||||
|
|
||||||
|
analyzer := &analysis.Analyzer{
|
||||||
|
Name: goHeaderName,
|
||||||
|
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||||
|
}
|
||||||
|
return goanalysis.NewLinter(
|
||||||
|
goHeaderName,
|
||||||
|
"Checks is file header matches to pattern",
|
||||||
|
[]*analysis.Analyzer{analyzer},
|
||||||
|
nil,
|
||||||
|
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||||
|
cfg := lintCtx.Cfg.LintersSettings.Goheader
|
||||||
|
c := &goheader.Configuration{
|
||||||
|
Values: cfg.Values,
|
||||||
|
Template: cfg.Template,
|
||||||
|
TemplatePath: cfg.TemplatePath,
|
||||||
|
}
|
||||||
|
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||||
|
if c.TemplatePath == "" && c.Template == "" {
|
||||||
|
// User did not pass template, so then do not run go-header linter
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
template, err := c.GetTemplate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
values, err := c.GetValues()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
a := goheader.New(goheader.WithTemplate(template), goheader.WithValues(values))
|
||||||
|
var res []goanalysis.Issue
|
||||||
|
for _, file := range pass.Files {
|
||||||
|
i := a.Analyze(file)
|
||||||
|
issue := result.Issue{
|
||||||
|
Pos: token.Position{
|
||||||
|
Line: i.Location().Line + 1,
|
||||||
|
Column: i.Location().Position,
|
||||||
|
Filename: pass.Fset.Position(file.Pos()).Filename,
|
||||||
|
},
|
||||||
|
Text: i.Message(),
|
||||||
|
FromLinter: goHeaderName,
|
||||||
|
}
|
||||||
|
res = append(res, goanalysis.NewIssue(&issue, pass))
|
||||||
|
}
|
||||||
|
if len(res) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
issues = append(issues, res...)
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||||
|
return issues
|
||||||
|
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||||
|
}
|
@ -202,6 +202,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||||||
WithPresets(linter.PresetFormatting).
|
WithPresets(linter.PresetFormatting).
|
||||||
WithAutoFix().
|
WithAutoFix().
|
||||||
WithURL("https://godoc.org/golang.org/x/tools/cmd/goimports"),
|
WithURL("https://godoc.org/golang.org/x/tools/cmd/goimports"),
|
||||||
|
linter.NewConfig(golinters.NewGoHeader()).
|
||||||
|
WithPresets(linter.PresetStyle).
|
||||||
|
WithLoadForGoAnalysis().
|
||||||
|
WithURL("https://github.com/denis-tingajkin/go-header"),
|
||||||
linter.NewConfig(golinters.NewMaligned()).
|
linter.NewConfig(golinters.NewMaligned()).
|
||||||
WithLoadForGoAnalysis().
|
WithLoadForGoAnalysis().
|
||||||
WithPresets(linter.PresetPerformance).
|
WithPresets(linter.PresetPerformance).
|
||||||
|
@ -173,6 +173,12 @@ func buildConfigFromShortRepr(t *testing.T, repr string, config map[string]inter
|
|||||||
lastObj[keyParts[len(keyParts)-1]] = kv[1]
|
lastObj[keyParts[len(keyParts)-1]] = kv[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func skipMultilineComment(scanner *bufio.Scanner) {
|
||||||
|
for line := scanner.Text(); !strings.Contains(line, "*/") && scanner.Scan(); {
|
||||||
|
line = scanner.Text()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func extractRunContextFromComments(t *testing.T, sourcePath string) *runContext {
|
func extractRunContextFromComments(t *testing.T, sourcePath string) *runContext {
|
||||||
f, err := os.Open(sourcePath)
|
f, err := os.Open(sourcePath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -183,10 +189,13 @@ func extractRunContextFromComments(t *testing.T, sourcePath string) *runContext
|
|||||||
scanner := bufio.NewScanner(f)
|
scanner := bufio.NewScanner(f)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
|
if strings.HasPrefix(line, "/*") {
|
||||||
|
skipMultilineComment(scanner)
|
||||||
|
continue
|
||||||
|
}
|
||||||
if !strings.HasPrefix(line, "//") {
|
if !strings.HasPrefix(line, "//") {
|
||||||
return rc
|
return rc
|
||||||
}
|
}
|
||||||
|
|
||||||
line = strings.TrimPrefix(line, "//")
|
line = strings.TrimPrefix(line, "//")
|
||||||
if strings.HasPrefix(line, "args: ") {
|
if strings.HasPrefix(line, "args: ") {
|
||||||
assert.Nil(t, rc.args)
|
assert.Nil(t, rc.args)
|
||||||
|
6
test/testdata/configs/go-header.yml
vendored
Normal file
6
test/testdata/configs/go-header.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
linters-settings:
|
||||||
|
goheader:
|
||||||
|
template: MY {{title}}
|
||||||
|
values:
|
||||||
|
const:
|
||||||
|
title: TITLE.
|
4
test/testdata/go-header.go
vendored
Normal file
4
test/testdata/go-header.go
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*MY TITLE!*/ // ERROR "Expected:TITLE., Actual: TITLE!"
|
||||||
|
//args: -Egoheader
|
||||||
|
//config_path: testdata/configs/go-header.yml
|
||||||
|
package testdata
|
Loading…
x
Reference in New Issue
Block a user