aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.golangci.toml6
-rw-r--r--Makefile5
-rw-r--r--contrib/linters.go128
-rw-r--r--go.mod1
5 files changed, 140 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 2b1b7a4f..f3615f1e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
/aerc.debug
/wrap
/.aerc.d
+/linters.so
race.log.*
raw.log
*.1
diff --git a/.golangci.toml b/.golangci.toml
index 97c1f7fb..74cd03bb 100644
--- a/.golangci.toml
+++ b/.golangci.toml
@@ -11,9 +11,15 @@ enable = [
"errorlint", # check to ensure no problems with wrapped errors
"gocritic", # check for bugs, performance, and style issues
"gofmt", # check that gofmt is satisfied
+ "aerc", # aerc specific linters
]
[linters-settings.nolintlint]
allow-unused = false # don't allow nolint if not required
require-explanation = true # require an explanation when disabling a linter
requre-specific = true # linter exceptions must specify the linter
+
+[linters-settings.custom.aerc]
+path = "./linters.so"
+description = "Aerc specific linters"
+original-url = "git.sr.ht/~rjarry/aerc/contrib"
diff --git a/Makefile b/Makefile
index aa0ac4ae..49cc571b 100644
--- a/Makefile
+++ b/Makefile
@@ -65,8 +65,11 @@ dev:
fmt:
$(GO) run mvdan.cc/gofumpt -w .
+linters.so: contrib/linters.go
+ $(GO) build -buildmode=plugin -o linters.so contrib/linters.go
+
.PHONY: lint
-lint:
+lint: linters.so
@contrib/check-whitespace `git ls-files` && echo white space ok.
@$(GO) run mvdan.cc/gofumpt -d . | grep ^ \
&& echo The above files need to be formatted, please run make fmt && exit 1 \
diff --git a/contrib/linters.go b/contrib/linters.go
new file mode 100644
index 00000000..7876cab6
--- /dev/null
+++ b/contrib/linters.go
@@ -0,0 +1,128 @@
+package main
+
+import (
+ "go/ast"
+ "go/token"
+ "go/types"
+
+ "golang.org/x/tools/go/analysis"
+)
+
+var PanicAnalyzer = &analysis.Analyzer{
+ Name: "panic",
+ Doc: "finds goroutines that do not initialize the panic handler",
+ Run: runPanic,
+}
+
+func runPanic(pass *analysis.Pass) (interface{}, error) {
+ methods := make(map[token.Pos]string)
+ for _, file := range pass.Files {
+ ast.Inspect(file, func(n ast.Node) bool {
+ g, ok := n.(*ast.GoStmt)
+ if !ok {
+ return true
+ }
+
+ var block *ast.BlockStmt
+
+ expr := g.Call.Fun
+ if e, ok := expr.(*ast.ParenExpr); ok {
+ expr = e.X
+ }
+
+ switch e := expr.(type) {
+ case *ast.FuncLit:
+ block = e.Body
+ case *ast.SelectorExpr:
+ sel, ok := pass.TypesInfo.Selections[e]
+ if ok {
+ f, ok := sel.Obj().(*types.Func)
+ if ok {
+ methods[f.Pos()] = f.Name()
+ }
+ }
+ case *ast.Ident:
+ block = inlineFuncBody(e)
+ }
+
+ if block == nil {
+ return true
+ }
+
+ if !isPanicHandlerInstall(block.List[0]) {
+ pass.Report(panicDiag(block.Pos()))
+ }
+
+ return true
+ })
+ }
+ for _, file := range pass.Files {
+ ast.Inspect(file, func(n ast.Node) bool {
+ f, ok := n.(*ast.FuncDecl)
+ if !ok {
+ return false
+ }
+ _, found := methods[f.Name.Pos()]
+ if !found {
+ return false
+ }
+ delete(methods, f.Name.Pos())
+ if !isPanicHandlerInstall(f.Body.List[0]) {
+ pass.Report(panicDiag(f.Body.Pos()))
+ }
+ return false
+ })
+ }
+
+ return nil, nil
+}
+
+func panicDiag(pos token.Pos) analysis.Diagnostic {
+ return analysis.Diagnostic{
+ Pos: pos,
+ Category: "panic",
+ Message: "missing defer log.PanicHandler() as first statement",
+ }
+}
+
+func inlineFuncBody(s *ast.Ident) *ast.BlockStmt {
+ d, ok := s.Obj.Decl.(*ast.AssignStmt)
+ if !ok {
+ return nil
+ }
+ for _, r := range d.Rhs {
+ if f, ok := r.(*ast.FuncLit); ok {
+ return f.Body
+ }
+ }
+ return nil
+}
+
+func isPanicHandlerInstall(stmt ast.Stmt) bool {
+ d, ok := stmt.(*ast.DeferStmt)
+ if !ok {
+ return false
+ }
+ s, ok := d.Call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return false
+ }
+ i, ok := s.X.(*ast.Ident)
+ if !ok {
+ return false
+ }
+ return i.Name == "log" && s.Sel.Name == "PanicHandler"
+}
+
+// golang-lint required plugin api
+type analyzerPlugin struct{}
+
+// This must be implemented
+func (*analyzerPlugin) GetAnalyzers() []*analysis.Analyzer {
+ return []*analysis.Analyzer{
+ PanicAnalyzer,
+ }
+}
+
+// This must be defined and named 'AnalyzerPlugin'
+var AnalyzerPlugin analyzerPlugin
diff --git a/go.mod b/go.mod
index 10421e9a..cc6dcbac 100644
--- a/go.mod
+++ b/go.mod
@@ -39,6 +39,7 @@ require (
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
github.com/zenhack/go.notmuch v0.0.0-20211022191430-4d57e8ad2a8b
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
+ golang.org/x/tools v0.1.12
)
replace golang.org/x/crypto => github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3