Skip to content

tolowerequalfold: detect local ToLower/ToUpper aliases in literal comparisons - #37496

Merged
pelikhan merged 2 commits into
mainfrom
copilot/tolowerequalfold-linter-false-positive
Jun 7, 2026
Merged

tolowerequalfold: detect local ToLower/ToUpper aliases in literal comparisons#37496
pelikhan merged 2 commits into
mainfrom
copilot/tolowerequalfold-linter-false-positive

Conversation

Copilot AI commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

tolowerequalfold only reported direct strings.ToLower/ToUpper(...) == ... patterns and missed the same idiom when the case-converted value was first assigned to a local variable. This created inconsistent enforcement and left equivalent case-insensitive comparisons unflagged.

  • Analyzer: add one-level local alias detection

    • Track local identifiers defined from strings.ToLower(...) / strings.ToUpper(...).
    • Treat those identifiers as case-conversion operands in == / != checks.
    • Keep the rule conservative by only reporting alias-based cases when the opposite operand is a string literal.
  • Analyzer: preserve self-compare safety

    • Keep the existing same-operand guard for direct calls.
    • Apply equivalent guard behavior for alias-backed operands so v := strings.ToLower(x); v == x is not reported.
  • Test fixtures: cover new behavior

    • Add a positive fixture for alias + literal comparison (reported).
    • Add a negative fixture for alias + source variable self-compare (not reported).
  • Call-site cleanup for known missed cases

    • Convert the reported variable-based comparisons to strings.EqualFold(...) in:
      • pkg/parser/yaml_import.go
      • pkg/workflow/features.go
      • pkg/workflow/runs_on_validation.go
      • pkg/workflow/error_recovery.go
lower := strings.ToLower(name)
_ = lower == "alice" // now reported by tolowerequalfold

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix tolowerfold linter false negative for local variable comparisons tolowerequalfold: detect local ToLower/ToUpper aliases in literal comparisons Jun 7, 2026
Copilot AI requested a review from pelikhan June 7, 2026 05:48
@pelikhan
pelikhan marked this pull request as ready for review June 7, 2026 05:54
Copilot AI review requested due to automatic review settings June 7, 2026 05:54
@pelikhan
pelikhan merged commit 5d3dfa2 into main Jun 7, 2026
@pelikhan
pelikhan deleted the copilot/tolowerequalfold-linter-false-positive branch June 7, 2026 05:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR updates several string comparisons to use strings.EqualFold for case-insensitive equality and enhances the tolowerequalfold linter to detect comparisons against variables that alias strings.ToLower/ToUpper(...).

Changes:

  • Replaced some == checks (and some ToLower(...) == ... patterns) with strings.EqualFold(...).
  • Extended the tolowerequalfold analyzer to recognize local alias variables assigned from strings.ToLower/ToUpper(...).
  • Added analyzer testdata covering the new alias-detection behavior.
Show a summary per file
File Description
pkg/workflow/runs_on_validation.go Uses EqualFold in runner label validation logic.
pkg/workflow/features.go Uses EqualFold for deprecated feature flag handling.
pkg/workflow/error_recovery.go Uses EqualFold in validation severity classification.
pkg/parser/yaml_import.go Switches setup-steps filename checks to EqualFold on the basename.
pkg/linters/tolowerequalfold/tolowerequalfold.go Adds alias tracking so the analyzer catches lower := strings.ToLower(x); lower == "...".
pkg/linters/tolowerequalfold/testdata/src/tolowerequalfold/tolowerequalfold.go Adds testdata for alias-based comparisons.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 6/6 changed files
  • Comments generated: 7

for _, label := range labels {
lower := strings.ToLower(label)
if strings.HasPrefix(lower, "macos-") || lower == "macos" {
if strings.HasPrefix(lower, "macos-") || strings.EqualFold(lower, "macos") {
Comment thread pkg/workflow/features.go
// Inline sub-agents are now enabled by default and the corresponding
// frontmatter flag is deprecated/no-op.
if flagLower == "inline-agents" {
if strings.EqualFold(flagLower, "inline-agents") {

switch {
case lowerField == "engine" || strings.Contains(lowerReason, "invalid engine"):
case strings.EqualFold(lowerField, "engine") || strings.Contains(lowerReason, "invalid engine"):
Comment thread pkg/parser/yaml_import.go
base := filepath.Base(filePath)
lower := strings.ToLower(base)
return lower == "copilot-setup-steps.yml" || lower == "copilot-setup-steps.yaml"
return strings.EqualFold(base, "copilot-setup-steps.yml") || strings.EqualFold(base, "copilot-setup-steps.yaml")
Comment on lines +117 to +135
switch stmt.Tok {
case token.DEFINE:
if obj.Pos() != ident.Pos() {
delete(aliases, obj)
continue
}
rhs, ok := rhsExprForIndex(stmt.Rhs, i)
if !ok {
delete(aliases, obj)
continue
}
if arg, ok := caseConvArg(rhs); ok {
aliases[obj] = arg
} else {
delete(aliases, obj)
}
case token.ASSIGN:
delete(aliases, obj)
}
Comment on lines +81 to +89
func collectCaseConvAliases(pass *analysis.Pass) map[types.Object]ast.Expr {
aliases := make(map[types.Object]ast.Expr)
for _, file := range pass.Files {
ast.Inspect(file, func(node ast.Node) bool {
switch n := node.(type) {
case *ast.AssignStmt:
collectAliasesFromAssignStmt(pass, n, aliases)
case *ast.ValueSpec:
collectAliasesFromValueSpec(pass, n, aliases)
Comment on lines +16 to +17
lower := strings.ToLower(name)
_ = lower == "alice" // want `use strings\.EqualFold`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tolowerequalfold linter: false negative when ToLower/ToUpper result is stored in a local variable before comparison

3 participants