Skip to content

Commit 95628bd

Browse files
committed
Release 0.0.10
- Speed up exclusions; run only on top level declarations - Fix typos Signed-off-by: Oliver Eikemeier <eikemeier@fillmore-labs.com>
1 parent d0e52f2 commit 95628bd

8 files changed

Lines changed: 78 additions & 59 deletions

File tree

.golangci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ linters:
3131
strict: true
3232
printf:
3333
funcs:
34-
- fillmore-labs.com/zerolint/pkg/internal/visitor.newMessageCategorized
34+
- fillmore-labs.com/zerolint/pkg/internal/visitor.msgFormatf
3535
testifylint:
3636
enable-all: true
3737
disable:

pkg/internal/filter/filter_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// you may not use this file except in compliance with the License.
55
// You may obtain a copy of the License at
66
//
7-
// http://www.apache.org/licenses/LICENSE-2.0
7+
// http://www.apache.org/licenses/LICENSE-2.0
88
//
99
// Unless required by applicable law or agreed to in writing, software
1010
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414
//
1515
// SPDX-License-Identifier: Apache-2.0
16+
1617
package filter_test
1718

1819
import (

pkg/internal/passes/excluded/analyzer.go

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,24 @@
1717
package excluded
1818

1919
import (
20-
"errors"
2120
"reflect"
2221

2322
"fillmore-labs.com/zerolint/pkg/internal/filter"
2423
"golang.org/x/tools/go/analysis"
25-
"golang.org/x/tools/go/analysis/passes/inspect"
2624
)
2725

28-
// ErrNoInspectorResult is returned when the ast inspetor is missing.
29-
var ErrNoInspectorResult = errors.New("excluded: inspector result missing")
30-
3126
// zerolintMarker is the prefix for zerolint directives in comments ("zerolint:exclude").
3227
const zerolintMarker = "zerolint:"
3328

3429
// Analyzer provides information about which types should be excluded from further analysis
3530
// by other passes in the zerolint toolchain.
3631
var Analyzer = &analysis.Analyzer{ //nolint:gochecknoglobals
37-
Name: "excluded",
38-
Doc: "determine type exclusions for later passes",
39-
URL: "https://pkg.go.dev/fillmore-labs.com/zerolint/pkg/internal/passes/excluded",
40-
Run: run,
32+
Name: "excluded",
33+
Doc: "determine type exclusions for later passes",
34+
URL: "https://pkg.go.dev/fillmore-labs.com/zerolint/pkg/internal/passes/excluded",
35+
Run: run,
36+
RunDespiteErrors: true,
4137

42-
Requires: []*analysis.Analyzer{inspect.Analyzer},
4338
FactTypes: []analysis.Fact{(*excludeFact)(nil)},
4439
ResultType: reflect.TypeFor[filter.Filter](),
4540
}

pkg/internal/passes/excluded/analyzer_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ var TestAnalyzer = &analysis.Analyzer{ //nolint:gochecknoglobals
4444
Requires: []*analysis.Analyzer{inspect.Analyzer, Analyzer},
4545
}
4646

47-
var ErrNoExcludedResult = errors.New("result of excluded.Analyzer missing")
47+
var (
48+
ErrNoInspectorResult = errors.New("testanalyzer: inspector result missing")
49+
ErrNoExcludedResult = errors.New("testanalyzer: result of excluded.Analyzer missing")
50+
)
4851

4952
func run(pass *analysis.Pass) (any, error) {
5053
in, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)

pkg/internal/passes/excluded/fact.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package excluded
1818

1919
type excludeFact struct{} //zerolint:exclude // Marker type, [analysis.Fact] must be pointers.
2020

21-
// AFact implements [analysis.Fact].
21+
// AFact makes excludeFact satisfy the [analysis.Fact] interface such that it can be exported as a fact.
2222
func (*excludeFact) AFact() {}
2323

2424
var excluded excludeFact //nolint:gochecknoglobals // dummy fact

pkg/internal/passes/excluded/run.go

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ import (
2626
"fillmore-labs.com/zerolint/pkg/internal/filter"
2727
"fillmore-labs.com/zerolint/pkg/internal/set"
2828
"golang.org/x/tools/go/analysis"
29-
"golang.org/x/tools/go/analysis/passes/inspect"
30-
"golang.org/x/tools/go/ast/inspector"
3129
)
3230

3331
// run performs the analysis to identify excluded types.
@@ -37,35 +35,15 @@ import (
3735
// 3. Types with a "//zerolint:exclude" comment.
3836
// It exports an [excludeFact] fact for each newly identified excluded type in the current package
3937
// and returns a [filter.Filter] containing the token.Pos of all excluded type definitions.
40-
func run(pass *analysis.Pass) (any, error) { //nolint:cyclop
41-
addExclusions(pass)
42-
43-
in, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
44-
if !ok {
45-
return nil, ErrNoInspectorResult
46-
}
47-
48-
for decl := range inspector.All[*ast.GenDecl](in) {
49-
if decl.Tok != token.TYPE {
50-
continue
51-
}
38+
// In run.go
5239

53-
// Exclude via "//zerolint:exclude" comment.
54-
excludeAll := hasExcludeComment(decl.Doc)
55-
56-
for _, spec := range decl.Specs {
57-
if ts, ok := spec.(*ast.TypeSpec); ok {
58-
// Exclude cgo-generated types.
59-
if strings.HasPrefix(ts.Name.Name, "_Ctype_") {
60-
excludeType(pass, ts)
61-
62-
continue
63-
}
40+
func run(pass *analysis.Pass) (any, error) {
41+
addExclusions(pass)
6442

65-
// Exclude via "//zerolint:exclude" comment.
66-
if excludeAll || hasExcludeComment(ts.Doc) || hasExcludeComment(ts.Comment) {
67-
excludeType(pass, ts)
68-
}
43+
for _, f := range pass.Files {
44+
for _, decl := range f.Decls {
45+
if genDecl, ok := decl.(*ast.GenDecl); ok {
46+
processGenDecl(pass, genDecl)
6947
}
7048
}
7149
}
@@ -81,24 +59,52 @@ func run(pass *analysis.Pass) (any, error) { //nolint:cyclop
8159
return filter.New(excludedTypeDefs), nil
8260
}
8361

62+
func processGenDecl(pass *analysis.Pass, genDecl *ast.GenDecl) {
63+
if genDecl.Tok != token.TYPE {
64+
return
65+
}
66+
67+
// Exclude all via "//zerolint:exclude" comment on the type block.
68+
excludeAll := hasExcludeComment(genDecl.Doc)
69+
70+
for _, spec := range genDecl.Specs {
71+
ts, ok := spec.(*ast.TypeSpec)
72+
if !ok {
73+
continue
74+
}
75+
76+
// Exclude cgo-generated types.
77+
if strings.HasPrefix(ts.Name.Name, "_Ctype_") {
78+
excludeType(pass, ts)
79+
80+
continue
81+
}
82+
83+
// Exclude via "//zerolint:exclude" comment on the type spec itself or its associated comment.
84+
if excludeAll || hasExcludeComment(ts.Doc) || hasExcludeComment(ts.Comment) {
85+
excludeType(pass, ts)
86+
}
87+
}
88+
}
89+
8490
// hasExcludeComment checks if a comment group contains "zerolint:exclude".
8591
func hasExcludeComment(comments *ast.CommentGroup) bool {
8692
if comments == nil {
8793
return false
8894
}
8995

9096
for _, comment := range comments.List {
91-
i := strings.Index(comment.Text, zerolintMarker)
92-
if i < 0 {
97+
text := strings.TrimLeft(comment.Text, "/ ")
98+
if !strings.HasPrefix(text, zerolintMarker) {
9399
continue
94100
}
95101

96-
argsPart := strings.Fields(comment.Text[i+len(zerolintMarker):])
97-
if len(argsPart) == 0 {
102+
argsText, _, _ := strings.Cut(text[len(zerolintMarker):], " ")
103+
if len(argsText) == 0 {
98104
continue
99105
}
100106

101-
args := strings.Split(argsPart[0], ",")
107+
args := strings.Split(argsText, ",")
102108
if slices.Contains(args, "exclude") {
103109
return true
104110
}

pkg/internal/visitor/visit_funcdecl.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func (v *Visitor) visitFuncRecv(n *ast.FuncDecl) {
7272
case v.level.Below(level.Extended):
7373
return
7474

75-
case v.isLock(n):
75+
case isLock(n, elem):
7676
// Lock/Unlock methods on zero-sized types (typedef struct noCopy{}), are common
7777
// to create an embeddable marker that structures should not be copied.
7878
// Therefore, don't suggest removing the star from the receiver.
@@ -107,18 +107,31 @@ func (v *Visitor) isError(n *ast.FuncDecl) bool {
107107
return types.Identical(fn.Type(), errorFunc.Type())
108108
}
109109

110-
// isLock determines if the function declaration n is a Lock or Unlock method
111-
// with no parameters and no return values, a common signature for locking mechanisms.
112-
func (v *Visitor) isLock(n *ast.FuncDecl) bool {
113-
if len(n.Type.Params.List) != 0 || n.Type.Results != nil && len(n.Type.Results.List) != 0 {
114-
return false
110+
// isLock determines if the function declaration represents a Lock or Unlock
111+
// method on a pointer receiver to a struct type. This pattern is often used with
112+
// a zero-sized `noCopy` struct for embedding, and the receiver type is a pointer to that struct.
113+
func isLock(n *ast.FuncDecl, elem types.Type) bool {
114+
if hasParams(n.Type) || hasResults(n.Type) {
115+
return false // A method with parameters or results is not a standard Lock/Unlock.
115116
}
116117

117118
switch n.Name.Name {
118119
case "Lock", "Unlock":
119-
return true
120+
_, structPtrReceiver := elem.Underlying().(*types.Struct)
121+
122+
return structPtrReceiver // Only valid on struct types.
120123

121124
default:
122125
return false
123126
}
124127
}
128+
129+
// hasParams checks if the given function type has any parameters.
130+
func hasParams(f *ast.FuncType) bool {
131+
return len(f.Params.List) != 0
132+
}
133+
134+
// hasResults checks if the given function type has any results.
135+
func hasResults(f *ast.FuncType) bool {
136+
return f.Results != nil && len(f.Results.List) != 0
137+
}

pkg/zerolint/analyzer.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,17 @@ spec-compliant Go code.`
4444

4545
// New creates and returns a new [analysis.Analyzer] to detect pointers to zero-length types.
4646
func New(opts ...Option) *analysis.Analyzer {
47+
o := options{
48+
logger: log.Default(),
49+
}
50+
Options(opts).apply(&o)
51+
4752
a := &analysis.Analyzer{
4853
Name: Name,
4954
Doc: Doc,
5055
URL: "https://pkg.go.dev/fillmore-labs.com/zerolint/pkg/zerolint",
5156
Requires: []*analysis.Analyzer{inspect.Analyzer, excluded.Analyzer},
5257
}
53-
54-
o := options{logger: log.Default()}
55-
56-
Options(opts).apply(&o)
5758
a.Run = o.run(&a.Flags)
5859

5960
return a

0 commit comments

Comments
 (0)