Skip to content

lenstringzero coverage gap: len(s) != 0 is flagged but the identical idiom len(s) > 0 (and len(s) < 1 / <= 0) escapes — relation [Content truncated due to length] #40581

Description

@github-actions

Summary

pkg/linters/lenstringzero flags len(s) == 0s == "" and len(s) != 0s != "", but it only matches the equality operators (token.EQL / token.NEQ). The exactly-equivalent relational forms on strings escape:

Written Equivalent to Flagged today?
len(s) == 0 s == "" (empty) ✅ yes
len(s) != 0 s != "" (non-empty) ✅ yes
len(s) > 0 s != "" (non-empty) no
len(s) < 1 / len(s) <= 0 s == "" (empty) no
len(s) >= 1 s != "" (non-empty) no

Since len(s) >= 0 always holds for strings, len(s) > 0 and len(s) != 0 are semantically identical — yet only one is reported. That inconsistency is the gap.

Location

pkg/linters/lenstringzero/lenstringzero.go:40:

nodeFilter := []ast.Node{(*ast.BinaryExpr)(nil)}

insp.Preorder(nodeFilter, func(n ast.Node) {
    expr, ok := n.(*ast.BinaryExpr)
    if !ok {
        return
    }
    if expr.Op != token.EQL && expr.Op != token.NEQ {  // <-- only == and !=
        return
    }
    ...

Impact

len(s) > 0 is the most common non-empty idiom in Go, so this is the larger of the two cases by prevalence. Real string-typed sites exist in production, e.g.:

  • pkg/stringutil/stringutil.go:44if len(normalized) > 0 {
  • pkg/stringutil/sanitize.go:209if len(result) > 0 && ...

(The narrow scoping is plausibly intentional precisely because > 0 is so prevalent — see the tradeoff below.)

Recommendation (team to decide scope)

The equivalence is exact, so extending coverage is consistent with the linter's stated goal (prefer direct string comparison). Two viable directions:

  1. Extend the operator set to the relational forms, mapping to the correct empty/non-empty fixer:
    • len(s) > 0, len(s) >= 1, 0 < len(s), 1 <= len(s)s != ""
    • len(s) < 1, len(s) <= 0, 1 > len(s), 0 >= len(s)s == ""
    • reuse the existing literal-zero / yoda-swap and string-type guard (lenStringArg), and adjust cmpVerb + the suggested-fix operator accordingly.
  2. Document the boundary explicitly in the analyzer Doc and doc.go if > 0 is deliberately excluded as too noisy — so the asymmetry is an intentional, recorded decision rather than an apparent oversight.

This report favors a deliberate choice over the current silent asymmetry where != 0 is flagged but the identical > 0 is not.

Validation checklist

  • If extending: testdata len(s) > 0 → diagnostic + fix s != ""; len(s) < 1 → fix s == ""; yoda 0 < len(s) handled.
  • Non-string len(slice) > 0 still not flagged (the *types.Basic String guard already covers this).
  • len(s) >= 0 (always-true) and len(s) < 0 (always-false) are not mis-fixed.
  • go test ./pkg/linters/lenstringzero/... passes.

Effort

Small–medium — single file; the type guard, alias tracking, and yoda handling already exist and are reusable; main work is operator mapping + fixer text + testdata.

Not a duplicate of any open sergo issue (no existing lenstringzero issue).

Generated by 🤖 Sergo - Serena Go Expert · 276.1 AIC · ⌖ 11 AIC · ⊞ 5.8K ·

  • expires on Jun 27, 2026, 9:30 PM UTC-08:00

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

cookieIssue Monster Loves Cookies!sergo

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions