Files
a87917360a Inline mattermost-govet into the monorepo (#35869)
* inline mattermost-govet

* fix style issues

* simplify the openApiSync spec test

* README.md tweaks

* fix missing licenses

* simplify README.md

* trigger server-ci on tools/mattermost-govet/**

* Apply https://github.com/mattermost/mattermost-govet/commit/470cf78253f0aa85a4f4b5b50002682bf823e729

---------

Co-authored-by: Mattermost Build <[email protected]>
2026-04-01 13:24:22 +00:00

133 lines
2.1 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package mutexLock
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/analysis"
)
var Analyzer = &analysis.Analyzer{
Name: "mutexLock",
Doc: "check for cases where a mutex is left locked before returning",
Run: run,
}
func isMutex(varType string) bool {
switch varType {
case "sync.Mutex", "sync.RWMutex":
return true
}
return false
}
func isLock(methodName string) bool {
switch methodName {
case "Lock", "RLock":
return true
}
return false
}
func isUnlock(methodName string) bool {
switch methodName {
case "Unlock", "RUnlock":
return true
}
return false
}
type checkState struct {
lockPos token.Pos
unlockPos token.Pos
counter int
reported bool
}
func run(pass *analysis.Pass) (interface{}, error) {
for _, file := range pass.Files {
var m map[string]*checkState
check := func() {
for k, v := range m {
if v.counter > 0 {
if !v.reported {
pass.Reportf(v.lockPos, "possible return with mutex %s locked", k)
v.reported = true
}
}
}
}
ast.Inspect(file, func(node ast.Node) bool {
if node == nil {
return false
}
if _, ok := node.(*ast.FuncDecl); ok {
check()
m = map[string]*checkState{}
}
if _, ok := node.(*ast.ReturnStmt); ok {
check()
}
if e, ok := node.(*ast.CallExpr); ok {
se, ok := e.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
methodName := se.Sel.Name
var id *ast.Ident
switch e := se.X.(type) {
case *ast.SelectorExpr:
id = e.Sel
case *ast.Ident:
id = e
default:
return false
}
varName := id.Name
varType := pass.TypesInfo.Uses[id].Type().String()
if !isMutex(varType) {
return false
}
st := m[varName]
if st == nil {
st = &checkState{}
}
if isLock(methodName) {
st.lockPos = se.Pos()
st.counter++
} else if isUnlock(methodName) {
st.unlockPos = se.Pos()
st.counter--
}
m[varName] = st
return false
}
return true
})
check()
}
return nil, nil
}