Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions pkg/linters/seenmapbool/seenmapbool.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,25 @@ func collectSeenMapCandidates(pass *analysis.Pass, body *ast.BlockStmt) map[type
func findNonSetMaps(pass *analysis.Pass, body *ast.BlockStmt, candidates map[types.Object]ast.Node) map[types.Object]bool {
nonSetMaps := make(map[types.Object]bool)
ast.Inspect(body, func(n ast.Node) bool {
if valSpec, ok := n.(*ast.ValueSpec); ok {
for i, name := range valSpec.Names {
if i < len(valSpec.Values) {
markIfNonSetLiteral(pass, name, valSpec.Values[i], candidates, nonSetMaps)
Comment on lines +152 to +153
}
}
return true
}
assign, ok := n.(*ast.AssignStmt)
if !ok {
return true
}
for i, lhs := range assign.Lhs {
if ident, ok := lhs.(*ast.Ident); ok {
if i < len(assign.Rhs) {
markIfNonSetLiteral(pass, ident, assign.Rhs[i], candidates, nonSetMaps)
}
continue
}
indexExpr, ok := lhs.(*ast.IndexExpr)
if !ok {
continue
Expand All @@ -176,6 +190,36 @@ func findNonSetMaps(pass *analysis.Pass, body *ast.BlockStmt, candidates map[typ
return nonSetMaps
}

// markIfNonSetLiteral marks the candidate named by ident as a non-set map when
// the value it is initialized with is a composite literal containing an entry
// whose value is not the literal true.
func markIfNonSetLiteral(pass *analysis.Pass, ident *ast.Ident, value ast.Expr, candidates map[types.Object]ast.Node, nonSetMaps map[types.Object]bool) {
if ident.Name == "_" {
return
}
obj := pass.TypesInfo.ObjectOf(ident)
if obj == nil {
return
}
if _, isCandidate := candidates[obj]; !isCandidate {
return
}
lit, ok := value.(*ast.CompositeLit)
if !ok {
return
}
Comment on lines +207 to +210
for _, elt := range lit.Elts {
kv, ok := elt.(*ast.KeyValueExpr)
if !ok {
continue
}
if !isBoolTrue(kv.Value) {
nonSetMaps[obj] = true
return
}
}
}

// isMapStringBool returns true if t is map[string]bool.
func isMapStringBool(t types.Type) bool {
if t == nil {
Expand Down
12 changes: 12 additions & 0 deletions pkg/linters/seenmapbool/testdata/src/seenmapbool/seenmapbool.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ func GoodBoolMapWithFalse() {
_ = flags
}

func GoodBoolMapLiteralWithFalse() {
// The false value is embedded directly in the initial composite literal.
flags := map[string]bool{"enabled": true, "disabled": false}
_ = flags
}

func GoodBoolMapVarLiteralWithFalse() {
// Same, declared with var inside a function body.
var flags = map[string]bool{"enabled": true, "disabled": false}
_ = flags
}

func BadSetBoolInClosure() []string {
// Set-map declared inside a closure must be reported exactly once.
unique := func(in []string) []string {
Expand Down