Skip to content

trimleftright: widen heuristic to catch distinct-character prefix/suffix bugs#46536

Merged
pelikhan merged 6 commits into
mainfrom
copilot/fix-trimleftright-heuristic
Jul 19, 2026
Merged

trimleftright: widen heuristic to catch distinct-character prefix/suffix bugs#46536
pelikhan merged 6 commits into
mainfrom
copilot/fix-trimleftright-heuristic

Conversation

Copilot AI commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

looksSuspiciousCutset only fired when the cutset contained a repeated rune, silently missing the most common form of the bug — distinct-character literals like "http", "bar", "abc", "v1" that are almost certainly intended as prefixes/suffixes rather than character sets.

Changes

  • trimleftright.go — Replaces the repeated-rune proxy with an inverted approach: flag all multi-rune all-alphanumeric cutsets except three recognized character-class idioms:

    1. Pure decimal-digit sets ("012", "0123456789")
    2. Pure ASCII-vowel sets ("aeiou", "aei")
    3. Complete hex-letter alphabet — all six of af present (any case, optionally with digits): "abcdef", "ABCDEF", "0123456789abcdef"

    Partial hex subsets like "abc" (only 3 of 6 letters) are correctly flagged. Adds three focused helpers: isAllASCIIDigits, isAllASCIIVowels, isCompleteHexAlphabet.

  • testdata/…/trimleftright.go — Adds bad cases for "http", "abc", "txt", "v1"; adds good cases for "0123456789", "abcdef", "0123456789abcdef"; removes the now-incorrect goodNoRepeatedAlnum fixture.

  • trimleftright_internal_test.go — Expands TestLooksSuspiciousCutset from 6 to 23 cases covering all exception paths and the newly flagged patterns.

  • docs/adr/46465-add-trimleftright-linter.md — Updates the decision rationale and negative consequences to reflect the broader heuristic.

// Now flagged — distinct-char prefix bugs previously missed:
strings.TrimLeft(url, "http")   // want: use TrimPrefix
strings.TrimLeft(s, "abc")      // want: use TrimPrefix
strings.TrimRight(f, "txt")     // want: use TrimSuffix

// Still not flagged — recognized character-class idioms:
strings.TrimLeft(s, "aeiou")         // vowel class
strings.TrimLeft(s, "0123456789")    // digit class
strings.TrimLeft(s, "abcdef")        // complete hex-letter class

Copilot AI and others added 2 commits July 19, 2026 06:35
…fix/suffix bugs

Replace the repeated-rune proxy with a smarter heuristic:
- Remove the repeated-rune requirement that was missing common bugs
  like TrimLeft(s, "http"), TrimLeft(s, "abc"), TrimRight(s, "txt")
- Add three narrow exceptions for intentional character-class trimming:
  1. Pure decimal-digit sets (e.g. "0123456789")
  2. Pure ASCII-vowel sets (e.g. "aeiou")
  3. Complete hex-letter alphabet (all 6 of a-f present in any case,
     optionally with digits, e.g. "abcdef", "0123456789abcdef")
- Partial hex subsets like "abc" are correctly flagged

Update testdata with new bad cases (http, abc, txt, v1) and good cases
(digits, vowels, hex alphabets). Expand unit tests for looksSuspiciousCutset.
Update Analyzer.Doc string and ADR to reflect the revised decision.

Closes #46526

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…ent empty-slice edge cases

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix the TrimLeft/TrimRight cutset heuristic trimleftright: widen heuristic to catch distinct-character prefix/suffix bugs Jul 19, 2026
Copilot AI requested a review from pelikhan July 19, 2026 06:42
@pelikhan
pelikhan marked this pull request as ready for review July 19, 2026 06:54
Copilot AI review requested due to automatic review settings July 19, 2026 06: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

Broadens trimleftright detection while exempting recognized character-class cutsets.

Changes:

  • Flags distinct-character prefix and suffix mistakes.
  • Adds digit, vowel, and hexadecimal exceptions.
  • Expands tests and updates the ADR.
Show a summary per file
File Description
pkg/linters/trimleftright/trimleftright.go Implements the broader heuristic and exceptions.
pkg/linters/trimleftright/trimleftright_internal_test.go Expands heuristic unit cases.
pkg/linters/trimleftright/testdata/src/trimleftright/trimleftright.go Adds analyzer fixtures.
docs/adr/46465-add-trimleftright-linter.md Documents the revised decision.

Review details

Tip

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

  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Medium


// Suspicious: partial hex-letter subset (not the complete alphabet).
// "abc" is tested above via "abc prefix"; here we confirm the
// 5-letter subset also escapes the hex exception.
@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions github-actions Bot 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.

The heuristic replacement looks correct. The three helpers (isAllASCIIDigits, isAllASCIIVowels, isCompleteHexAlphabet) are straightforward and well-tested. Test coverage expands from 6 to 23 cases covering all exception paths.

The pre-existing inline comment about the inaccurate "5-letter subset" remark for abce (4 letters, not 5) is already flagged and should be fixed before merge.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 17.5 AIC · ⌖ 6.71 AIC · ⊞ 5K

@github-actions

Copy link
Copy Markdown
Contributor

Design Decision Gate — ADR Verified

ADR reviewed: ADR-46465: Add trimleftright Linter to Detect TrimLeft/TrimRight Cutset Misuse — implementation aligns with the stated decision. Great work!

Verification Summary

The PR updates the existing ADR to reflect the widened heuristic and the code changes faithfully implement the updated decision:

  • ADR Decision: Flag strings.TrimLeft/strings.TrimRight for all multi-rune all-alphanumeric cutsets except three recognised idioms: pure decimal-digit sets, pure ASCII-vowel sets, and the complete hex-letter alphabet (all six of a-f present).
  • looksSuspiciousCutset in trimleftright.go: Inverted logic now returns true for all alphanumeric multi-rune cutsets unless isAllASCIIDigits, isAllASCIIVowels, or isCompleteHexAlphabet applies — matches the ADR exactly.
  • Three new helpers (isAllASCIIDigits, isAllASCIIVowels, isCompleteHexAlphabet): Each encodes one of the three named exceptions, consistent with the ADR.
  • Test fixtures in trimleftright_internal_test.go expand to 23 cases covering all three safe-class idioms and the newly flagged distinct-character patterns (http, abc, v1, etc.) — aligns with the ADR consequence that partial hex subsets are flagged.
  • Testdata adds badHTTP, badABC, badTXT, badV1 and removes the now-incorrect goodNoRepeatedAlnum fixture, correctly reflecting the broader trigger.

No divergences found.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · 41.3 AIC · ⌖ 10.9 AIC · ⊞ 8.5K ·
Comment /review to run again

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 100/100 — Excellent

Analyzed 21 test(s): 21 design, 0 implementation, 0 violation(s).

📊 Metrics (21 tests)
Metric Value
Analyzed 21 (Go: 21 subtests, JS: 0)
✅ Design 21 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 21 (100%)
Duplicate clusters 0
Inflation No (0.75:1 ratio)
🚨 Violations 0

Test Classification

Test Group Count Classification Notes
Trivial safety (empty, single-char, whitespace, punctuation) 4 behavioral_contract, high_value, design Boundary cases
Repeated rune patterns (legacy heuristic) 2 behavioral_contract, high_value, design Tests core confusion detection
Distinct-char prefixes/suffixes (PR fix) 6 behavioral_contract, high_value, design Core fix: http, abc, txt, bar, v1, 0x
Intentional digit class exceptions 2 behavioral_contract, high_value, design Complete vs partial coverage
Intentional vowel class exceptions 3 behavioral_contract, high_value, design Case-insensitive boundaries
Complete hex alphabet exceptions 5 behavioral_contract, high_value, design 6-letter boundary, case variations
Partial hex subset (boundary test) 1 behavioral_contract, high_value, design Confirms 5-letter is not safe

Verdict

APPROVED. All 21 subtests are design tests (100%). 0% implementation tests (threshold: 30%). Zero violations.

Highlights:

  • All tests verify behavioral contracts (guard-rail against TrimLeft/TrimRight confusion)
  • Comprehensive edge-case coverage: empty, single-char, boundaries between safe/suspicious
  • Distinct-char patterns (http, abc, txt, bar, v1, 0x) directly test the PR's main fix
  • Excellent deduplication: removed "partial hex abc" duplicate (already covered by "abc prefix")
  • Clear assertions with descriptive failure messages
  • Test inflation ratio 0.75:1 (well below 2:1 threshold)
  • No mocking library violations or Go guideline breaches

The test suite directly validates that the new heuristic catches real bugs that the old implementation missed.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 18.2 AIC · ⌖ 8.71 AIC · ⊞ 7K ·
Comment /review to run again

@github-actions github-actions Bot 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.

✅ Test Quality Sentinel: 100/100. All 21 subtests are behavioral-contract tests (0% implementation tests). Zero violations.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category bug
Risk 🟢 low
Score 55/100 (impact:25 urgency:15 quality:15)
Action auto_merge

CI passing ✅. Small focused fix (+154/-21, 4 files). Bot reviewer commented. Auto-merge candidate once CI gates pass.


Automated triage — run §29677420780

Generated by 🔧 PR Triage Agent · 34.2 AIC · ⌖ 5.12 AIC · ⊞ 5.6K ·

@github-actions github-actions Bot 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.

One correctness regression must be fixed before merge.

The logic is sound and the test coverage is good. One issue needs to be addressed:

  • isCompleteHexAlphabet regresses repeated-rune detection — cutsets like "aabbccddeeff" pass the hex exception silently because the map deduplicates; the old repeated-rune check would have flagged these. See inline comment for a fix.

🔎 Code quality review by PR Code Quality Reviewer · 34.9 AIC · ⌖ 4.69 AIC · ⊞ 5.6K
Comment /review to run again

return false
}
}
return len(seen) == 6

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.

Regression: isCompleteHexAlphabet silently accepts suspicious repeated-rune hex cutsets — e.g. "aabbccddeeff" now passes the hex exception when it would have been flagged by the old repeated-rune check.

💡 Details and fix

The hex exception is intended to recognise intentional hex-class trimming like "abcdef". But the current implementation only checks that all 6 hex letters appear in seen; it never rejects duplicate runes. A cutset like "aabbccddeeff" populates seen with all 6 letters and returns false (not suspicious), silently un-flagging what is almost certainly a bug.

The old code caught this class via the repeated-rune check; widening the heuristic regressed it.

Simple fix — add a uniqueness check:

func isCompleteHexAlphabet(runes []rune) bool {
    if len(runes) > 16 { // 6 hex letters + 10 digits maximum
        return false
    }
    seen := make(map[rune]struct{})
    for _, r := range runes {
        lower := unicode.ToLower(r)
        switch {
        case lower >= 'a' && lower <= 'f':
            if _, dup := seen[lower]; dup {
                return false // repeated hex letter → suspicious
            }
            seen[lower] = struct{}{}
        case r >= '0' && r <= '9':
        default:
            return false
        }
    }
    return len(seen) == 6
}

Add a test case: {name: "repeated hex letters", cutset: "aabbccddeeff", want: true}.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit 322ddb3: isCompleteHexAlphabet now has a len(runes) > 16 guard and a per-letter uniqueness check, so "aabbccddeeff" (and any other repeated-hex-letter cutset) is correctly returned as suspicious. The test case {name: "repeated hex letters", cutset: "aabbccddeeff", want: true} was also added.

@github-actions github-actions Bot 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.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — the fix is solid and well-tested; leaving comments on minor coverage gaps.

📋 Key Themes & Highlights

Key Themes

  • Inverted heuristic is correct: flagging all multi-rune alphanumeric except known safe idioms is a much better model than the previous repeated-rune proxy.
  • Minor test coverage gaps: the boundary of isCompleteHexAlphabet (duplicate hex letters, all-six-plus-one) is untested and undocumented.
  • ADR update is thorough: the negative-consequence note now accurately reflects the trade-off.

Positive Highlights

  • ✅ Three narrowly-scoped helper functions — each with a single, clearly-stated responsibility
  • ✅ Test count grew from 6 → 23 with grouped, readable categories
  • ✅ Existing regression cases ("foo", "barr") retained alongside new ones
  • ✅ ADR updated to reflect the changed heuristic rationale

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 38.9 AIC · ⌖ 4.55 AIC · ⊞ 6.7K
Comment /matt to run again

// Suspicious: partial hex-letter subset (not the complete alphabet).
// "abc" is tested above via "abc prefix"; here we confirm the
// 5-letter subset also escapes the hex exception.
{name: "partial hex abce", cutset: "abce", want: true},

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.

[/tdd] Missing boundary test: "abcdefg" should be flagged (suspicious) but there is no test to confirm it escapes the hex exception.

💡 Suggested test case

Add to the suspicious section:

{name: "almost hex abcdefg", cutset: "abcdefg", want: true},

This documents that adding any non-hex-letter to all-six-hex-letters still triggers the linter, locking down the boundary of isCompleteHexAlphabet.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added {name: "almost hex abcdefg", cutset: "abcdefg", want: true} in commit trimleftright: fix regression in isCompleteHexAlphabet, add boundary tests.

return false
}
}
return len(seen) == 6

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.

[/diagnosing-bugs] isCompleteHexAlphabet silently deduplicates: a cutset like "aabcdef" (duplicate 'a', all six distinct hex letters present) returns false (safe). This is probably correct, but is untested and undocumented — a future reader may wonder whether duplicates were forgotten or intentional.

💡 Suggested test + doc note

Add to the safe-hex block in TestLooksSuspiciousCutset:

{name: "hex letters with duplicate", cutset: "aabcdef", want: false},

Add a comment inside isCompleteHexAlphabet to make intent explicit:

// Duplicate hex letters are allowed; only distinct coverage of all six matters.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The regression fix in thread r3610090990 now explicitly rejects duplicate hex letters, so "aabcdef" is flagged as suspicious (want: true) rather than safe. I added the test as {name: "hex with duplicate letter", cutset: "aabcdef", want: true} and updated the function comment to document that repeated hex letters are rejected. Both changes are in commit trimleftright: fix regression in isCompleteHexAlphabet, add boundary tests.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address the unresolved review feedback below, and rerun checks once the branch is ready for maintainer review.

Unresolved review feedback:

The latest maintainer comment asked for pr-finisher; please incorporate any remaining fixes and summarize the next concrete maintainer action.

Generated by PR Sous Chef: https://github.com/github/gh-aw/actions/runs/29677770339

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • pi.dev

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "pi.dev"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · 20.6 AIC · ⌖ 8.43 AIC · ⊞ 6K ·
Comment /souschef to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Fixed the inaccurate comment — abce has 4 letters, so the comment now reads "4-letter subset" (commit trimleftright: fix comment — abce is a 4-letter subset, not 5-letter). Local make fmt, make lint, and make test-unit all pass.

…tests

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address the unresolved review feedback below, and rerun checks once the branch is ready for maintainer review.

Unresolved review feedback:...

All three github-actions[bot] threads addressed in commit trimleftright: fix regression in isCompleteHexAlphabet, add boundary tests:

  • r3610090990 — Fixed the regression: isCompleteHexAlphabet now has a length guard (> 16) and a uniqueness check for hex letters, so "aabbccddeeff" is correctly flagged.
  • r3610091397 — Added boundary test "abcdefg"want: true.
  • r3610091401 — Since the regression fix now rejects duplicate hex letters, "aabcdef"want: true (not false as the bot originally suggested). Added that test and updated the function comment to document the behavior.

Local make fmt, make lint, and make test-unit all pass. CI needs a human re-trigger before merge.

Copilot AI requested a review from gh-aw-bot July 19, 2026 07:45
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, verify the current HEAD still addresses the blocking review feedback below, and summarize the exact maintainer step needed to get this PR merged.

Unresolved review feedback:

The thread set appears stale relative to recent author follow-up, so please confirm whether each thread can now be resolved and hand back the next concrete maintainer action.

Generated by PR Sous Chef: https://github.com/github/gh-aw/actions/runs/29679264083

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • pi.dev

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "pi.dev"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · 13 AIC · ⌖ 7.35 AIC · ⊞ 7.5K ·
Comment /souschef to run again

Copilot AI commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, verify the current HEAD still addresses the blocking review feedback below, and summarize the exact maintainer step needed to get this PR merged.

Unresolv...

All four threads are addressed in the current HEAD (322ddb3):

  • r3610067714 (Copilot, outdated) — Comment corrected from "5-letter" to "4-letter" in commit 219a638. Thread is marked outdated by GitHub because the line changed.
  • r3610090990 (regression) — isCompleteHexAlphabet now has a length guard and uniqueness check; "aabbccddeeff" is correctly flagged. Test added.
  • r3610091397 (boundary test) — "abcdefg"want: true test added.
  • r3610091401 (duplicate hex) — "aabcdef"want: true (duplicate letters are now explicitly rejected); function comment updated; test added.

Local make fmt, make lint, and make test-unit all pass. The only remaining maintainer action is to re-trigger CI (close/reopen the PR, workflow_dispatch, or push) and then merge once checks are green.

@pelikhan
pelikhan merged commit af66860 into main Jul 19, 2026
18 checks passed
@pelikhan
pelikhan deleted the copilot/fix-trimleftright-heuristic branch July 19, 2026 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants