Skip to content

[linter-miner] feat(linters): add appendoneelement linter#46289

Merged
pelikhan merged 4 commits into
mainfrom
linter-miner/appendoneelement-b43dce164fee9c1b
Jul 18, 2026
Merged

[linter-miner] feat(linters): add appendoneelement linter#46289
pelikhan merged 4 commits into
mainfrom
linter-miner/appendoneelement-b43dce164fee9c1b

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

test

Generated by PR Description Updater for #46289 · 63.4 AIC · ⌖ 6.71 AIC · ⊞ 4.7K ·

Adds a new linter that flags append(s, []T{x}...) patterns where
a single-element slice literal is spread unnecessarily. These can
be simplified to append(s, x), which is more idiomatic Go.

The linter:
- Detects append calls with exactly 2 args + ellipsis
- Checks when the second arg is a composite slice literal with 1 element
- Provides a suggested fix rewriting the call to the simpler form
- Skips generated files and respects //nolint:appendoneelement directives

Registers the analyzer in cmd/linters/main.go.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

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

@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

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

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

Adds the appendoneelement Go analyzer to detect and simplify unnecessary single-element slice expansion.

Changes:

  • Implements and registers the analyzer.
  • Adds analyzer fixtures and suggested-fix tests.
  • Handles generated files and nolint directives.
Show a summary per file
File Description
cmd/linters/main.go Registers the analyzer.
pkg/linters/appendoneelement/appendoneelement.go Implements detection and fixes.
pkg/linters/appendoneelement/appendoneelement_test.go Runs analyzer tests.
pkg/linters/appendoneelement/testdata/src/appendoneelement/appendoneelement.go Provides test cases.
pkg/linters/appendoneelement/testdata/src/appendoneelement/appendoneelement.go.golden Defines expected fixes.

Review details

Tip

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

Comments suppressed due to low confidence (1)

pkg/linters/appendoneelement/appendoneelement.go:106

  • Replacing the entire call with printer-generated text silently drops comments and source structure outside the redundant literal. For example, append(s /* why this destination */, []int{3}...) is fixed to append(s, 3), deleting the comment. Apply narrow deletions to the literal type/braces and ellipsis instead so comments on the destination and around the element survive.
						Pos:     call.Pos(),
						End:     call.End(),
						NewText: []byte(fmt.Sprintf("append(%s, %s)", sliceText, elemText)),
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment on lines +77 to +81
if len(lit.Elts) != 1 {
return
}

elem := lit.Elts[0]

@github-actions github-actions Bot left a comment

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 implementation is correct and follows existing linter conventions. Logic, tests, and golden fixtures all look good.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 19.5 AIC · ⌖ 4.91 AIC · ⊞ 5K

@github-actions github-actions Bot left a comment

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.

Skills-Based Review 🧠

Applied /tdd and /grill-with-docs — requesting changes on two small issues.

📋 Key Themes & Highlights

Issues Found

  • Misleading *ast.ArrayType comment (line 68): the comment says "not a map/struct/array" but *ast.ArrayType matches both slices (Len == nil) and fixed-size arrays (Len != nil). A Len != nil guard should be added to make the intent explicit and future-proof.
  • Missing suppression regression test: no fixture tests the //nolint:appendoneelement path, leaving a silent regression surface.

Positive Highlights

  • ✅ Clean implementation following the established pkg/linters/ pattern
  • ✅ Golden-file test with analysistest.RunWithSuggestedFixes is the right approach
  • ✅ Good test coverage of the happy path (int, string, variable element)
  • ✅ Correct use of filecheck.ShouldSkipFilename and nolint.HasDirectiveForLinter
  • ✅ Zero-false-positive design: only triggers on single-element composite literals

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

return
}

// The second argument must be a composite literal with exactly one element.

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.

[/tdd] The comment says "Must be a slice type, not a map/struct/array" but *ast.ArrayType in Go's AST represents both []T (slice, Len == nil) and [N]T (fixed-size array, Len != nil). The guard works in practice because a fixed-size array literal cannot be spread in append, but the comment is misleading.

💡 Suggested fix

Add a Len == nil check and update the comment to be precise:

// Must be a slice type ([]T). *ast.ArrayType covers both slices and
// fixed-size arrays; only slices have Len == nil.
arrType, ok := lit.Type.(*ast.ArrayType)
if !ok || arrType.Len != nil {
    return
}

This makes the intent explicit and guards against any future compiler relaxation.

@copilot please address this.


func TestAnalyzer(t *testing.T) {
testdata := analysistest.TestData()
analysistest.RunWithSuggestedFixes(t, testdata, appendoneelement.Analyzer, "appendoneelement")

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.

[/tdd] The test suite covers happy-path cases but has no negative test for //nolint:appendoneelement suppression. This code path exists in the implementation and can silently regress without a fixture.

💡 Suggested addition to testdata

Add to testdata/src/appendoneelement/appendoneelement.go:

func nolintCase() {
	s := []int{1}
	s = append(s, []int{2}...) (nolint/redacted):appendoneelement
	_ = s
}

This should produce no diagnostic. Without this, a regression in nolint.HasDirectiveForLinter goes undetected.

@copilot please address this.

@github-actions

Copy link
Copy Markdown
Contributor Author

🧪 Test Quality Sentinel Report

Test Quality Score: 95/100 — Excellent

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

📊 Metrics (1 test)
Metric Value
Analyzed 1 (Go: 1, JS: 0)
✅ Design 1 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 1 (100%)
Duplicate clusters 0
Inflation No
🚨 Violations 0
Test File Classification Issues
TestAnalyzer pkg/linters/appendoneelement/appendoneelement_test.go:13 design_test / behavioral_contract / high_value None

Verdict

Passed. 0% implementation tests (threshold: 30%). No violations.

TestAnalyzer uses analysistest.RunWithSuggestedFixes — the idiomatic pattern for Go analysis linters. The accompanying testdata covers 3 positive (flag) cases (int literal, string literal, variable) and 3 negative (no false positive) cases (multi-element spread, direct append, variable spread), plus a .golden file verifying the suggested-fix output. Build tag //go:build !integration is present on line 1. No mock libraries used. Test:prod line ratio is well under 2:1.

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 · 28.1 AIC · ⌖ 9.57 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

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.

✅ Test Quality Sentinel: 95/100. 0% implementation tests (threshold: 30%). No violations.

@github-actions github-actions Bot left a comment

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.

Two correctness issues must be fixed before merge

1. Built-in name check is purely syntactic — The ident.Name == "append" guard at line 53 does not verify the identifier resolves to the built-in. Code that shadows append with a local variadic function will be incorrectly flagged and have its call site rewritten by the suggested fix, breaking the program silently. Use pass.TypesInfo.ObjectOf(ident) and check obj.Pkg() == nil.

2. *ast.ArrayType includes fixed-size arrays — The slice-type guard at line 74 accepts both []T and [N]T because both are *ast.ArrayType. Add arrayType.Len != nil check to filter out fixed-size arrays.

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


// Must be append(x, y...) with exactly 2 arguments and ellipsis.
ident, ok := call.Fun.(*ast.Ident)
if !ok || ident.Name != "append" {

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.

Shadowed built-in not detected: checking ident.Name == "append" is purely syntactic — if a package defines a local function named append, this linter will fire on calls to it and emit an incorrect suggested fix.

💡 Suggested fix

Use the type checker to verify the callee is the built-in:

obj := pass.TypesInfo.ObjectOf(ident)
if obj == nil {
    return // unresolved
}
if obj.Pkg() != nil {
    return // not a built-in
}

Or equivalently, use types.Universe.Lookup("append") and confirm obj == types.Universe.Lookup("append").

Without this guard the analyzer's suggested fix silently corrupts calls to user-defined variadic functions that happen to be named append.

return
}
// Must be a slice type, not a map/struct/array.
if _, ok := lit.Type.(*ast.ArrayType); !ok {

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.

*ast.ArrayType matches fixed-size arrays, not just slices: the comment says "not a map/struct/array" but [N]T is also an *ast.ArrayType — the linter will flag append(s, [1]int{x}...) even though spreading a fixed-size array is semantically different and cannot be simplified the same way.

💡 Suggested fix

Check that Len is nil (which distinguishes a slice type from a fixed-size array):

arrayType, ok := lit.Type.(*ast.ArrayType)
if !ok || arrayType.Len != nil {
    return // not a slice literal
}

Spreading [1]int{x}... is already a compile error in real Go code (arrays are not variadic-spreadable), so this guard is defensive but makes the intent explicit and future-proof against unexpected AST shapes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor Author

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (214 new lines in pkg/linters/) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/46289-add-appendoneelement-linter.md — review and complete it before merging.

🔒 This PR cannot merge until an ADR is linked in the PR body.

📋 What to do next
  1. Review the draft ADR committed to your branch — it was generated from the PR diff
  2. Complete the missing sections — add context the AI could not infer, refine the decision rationale, and list real alternatives you considered
  3. Commit the finalized ADR to docs/adr/ on your branch
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-46289: Add appendoneelement Linter

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

❓ Why ADRs Matter

“AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say ‘I’ll deal with this later.’ Deferring decisions corroded my ability to think clearly.”

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

📋 Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 46289-add-appendoneelement-linter.md for PR #46289).

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

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run or-finisher skill

Copilot AI and others added 2 commits July 18, 2026 11:20
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

@copilot run or-finisher skill

Ran the finisher flow and pushed fixes in 745ef17 (addressed analyzer edge cases and added regression fixtures/tests).

Copilot AI requested a review from pelikhan July 18, 2026 11:38
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address the outstanding review feedback (built-in append shadowing, fixed-size array guard, suppression regression coverage, and ADR follow-up), and rerun checks when done.

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

@pelikhan
pelikhan merged commit b5cc05b into main Jul 18, 2026
23 of 24 checks passed
@pelikhan
pelikhan deleted the linter-miner/appendoneelement-b43dce164fee9c1b branch July 18, 2026 11:53
Copilot stopped work on behalf of gh-aw-bot due to an error July 18, 2026 11:53
Copilot AI requested a review from gh-aw-bot July 18, 2026 11:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants