Skip to content

fix(functional-pragmatist): replace make+len patterns to avoid CodeQL violations#23685

Merged
pelikhan merged 3 commits intomainfrom
copilot/fix-functional-pragmatist-codeql-violations
Mar 31, 2026
Merged

fix(functional-pragmatist): replace make+len patterns to avoid CodeQL violations#23685
pelikhan merged 3 commits intomainfrom
copilot/fix-functional-pragmatist-codeql-violations

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 31, 2026

CodeQL flags make([]T, len(x)) and make([]T, 0, len(x)) as violations. The functional-pragmatist workflow was actively recommending these patterns in helper examples and guidelines.

Changes

  • Map helper: make([]U, len(slice)) + index assignment → var result []U + append
  • Filter helper: make([]T, 0, len(slice))var result []T + append
  • Example transformation: make([]Filter, len(names)) + index assignment → var filters []Filter + append
  • Inline example: make([]string, 0, len(items))var activeNames []string
  • Defensive copy: make([]Item, len(s.items)) + copyslices.Clone(s.items)
  • Guideline: replaced the recommendation to preallocate with make([]T, len(input)) with an explicit warning explaining why CodeQL flags these patterns
// Before
func Map[T, U any](slice []T, fn func(T) U) []U {
    result := make([]U, len(slice))
    for i, v := range slice {
        result[i] = fn(v)
    }
    return result
}

// After
// Note: uses var+append to avoid CodeQL violations from make([]U, len(slice))
func Map[T, U any](slice []T, fn func(T) U) []U {
    var result []U
    for _, v := range slice {
        result = append(result, fn(v))
    }
    return result
}

Copilot AI and others added 2 commits March 31, 2026 12:13
…eline and Map trade-off note

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/acdcfee6-bf25-4163-be26-348e308ab7aa

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix functional-pragmatist workflow to avoid CodeQL violations fix(functional-pragmatist): replace make+len patterns to avoid CodeQL violations Mar 31, 2026
Copilot AI requested a review from pelikhan March 31, 2026 12:15
@github-actions github-actions bot mentioned this pull request Mar 31, 2026
@pelikhan pelikhan marked this pull request as ready for review March 31, 2026 12:20
Copilot AI review requested due to automatic review settings March 31, 2026 12:20
@pelikhan pelikhan merged commit cdbb6d0 into main Mar 31, 2026
51 checks passed
@pelikhan pelikhan deleted the copilot/fix-functional-pragmatist-codeql-violations branch March 31, 2026 12:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the functional-pragmatist workflow guidance/examples to avoid slice preallocation patterns (make([]T, len(x)), make([]T, 0, len(x))) that are flagged by CodeQL.

Changes:

  • Rewrites Map/Filter helper examples to build results via var + append instead of pre-sized/pre-cap slices.
  • Updates “immutability” and inline-loop examples to remove make(..., len(...)) / make(..., 0, len(...)) patterns.
  • Replaces a defensive-copy snippet (make + copy) with slices.Clone, and updates the guideline bullet to warn against preallocation patterns.
Comments suppressed due to low confidence (2)

.github/workflows/functional-pragmatist.md:474

  • Similar to Map, switching Filter to var result []T changes the behavior when there are no matches (or the input is empty): it can return nil rather than a non-nil empty slice. If this is meant as a copy/paste helper implementation, it’s usually better to keep the empty-slice semantics stable (or call out this difference explicitly) to avoid surprises in callers/tests/marshalling.
// Filter returns elements that match the predicate
func Filter[T any](slice []T, fn func(T) bool) []T {
    var result []T
    for _, v := range slice {
        if fn(v) {

.github/workflows/functional-pragmatist.md:1392

  • Switching the defensive-copy example to slices.Clone(s.items) subtly changes nil/empty behavior compared to make+copy: if s.items is nil, slices.Clone returns nil (whereas make([]Item, 0) would return a non-nil empty slice). If this snippet is meant to preserve “always return a slice” semantics for callers/JSON, consider calling that out or using a pattern that keeps empty-slice semantics. Also, if the motivation is “avoid user-controlled allocation sizes”, Clone still allocates proportional to len(s.items)—the mitigation should be validating/capping untrusted lengths, not just changing the API used.
// Return copy to prevent mutation of internal state
func (s *Service) GetItems() []Item {
    return slices.Clone(s.items)
}

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

Comment on lines +461 to +465
// Note: uses var+append to avoid CodeQL violations from make([]U, len(slice))
func Map[T, U any](slice []T, fn func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = fn(v)
var result []U
for _, v := range slice {
result = append(result, fn(v))
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The updated Map example uses a nil slice (var result []U) and appends into it. This changes the empty-input behavior vs the previous make([]U, len(slice)): Map([]T{}, ...) will now return nil instead of a non-nil empty slice, which can affect callers (e.g., JSON null vs [], reflect.DeepEqual, or equality assertions). If you want to keep the prior semantics while still avoiding capacity/length derived from the input, consider initializing to an empty (non-nil) slice without using len(slice) for sizing, or explicitly documenting the nil-vs-empty behavior change in the guidance.

This issue also appears in the following locations of the same file:

  • line 470
  • line 1389

Copilot uses AI. Check for mistakes.
Comment on lines +511 to 515
// After: Immutable initialization using append
var filters []Filter
for _, name := range names {
filters = append(filters, Filter{Name: name})
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

In this “Example transformations” snippet, the “Before” and “After” blocks are now effectively identical (both declare var filters []Filter and then append in a loop), so it no longer demonstrates a transformation. Also, the label “Immutable initialization using append” is misleading because the slice variable is still being mutated. Consider either restoring a contrasting “Before” example, or making the “After” example the functional helper call (and renaming the label to avoid implying immutability).

See below for a potential fix:

// Before: Mutable slice construction with index-based assignment
filters := make([]Filter, len(names))
for i, name := range names {
    filters[i] = Filter{Name: name}
}

// After: Functional slice initialization

Copilot uses AI. Check for mistakes.
Comment on lines 571 to 575
activeNames := sliceutil.Map(activeItems, func(item Item) string { return item.Name })

// Or inline if it's clearer:
activeNames := make([]string, 0, len(items))
var activeNames []string
for _, item := range items {
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

This single Go code block declares activeNames multiple times (var activeNames []string, then later activeNames := ..., then again var activeNames []string). If someone copies this block into a function, it won’t compile due to redeclaration in the same scope. Consider splitting alternatives into separate code blocks or using distinct variable names per alternative (e.g., activeNamesImperative / activeNamesPipeline).

See below for a potential fix:

var activeNamesImperative []string
for _, item := range items {
    if item.Active {
        activeNamesImperative = append(activeNamesImperative, item.Name)
    }
}

// After: Functional pipeline
activeItems := sliceutil.Filter(items, func(item Item) bool { return item.Active })
activeNamesPipeline := sliceutil.Map(activeItems, func(item Item) string { return item.Name })

// Or inline if it's clearer:
var activeNamesInline []string
for _, item := range items {
    if item.Active {
        activeNamesInline = append(activeNamesInline, item.Name)

Copilot uses AI. Check for mistakes.
- Inline loops are often clearer than generic helpers
- Use type parameters (generics) for helpers to avoid reflection
- Preallocate slices when size is known: `make([]T, len(input))`
- Avoid `make([]T, len(input))` and `make([]T, 0, len(input))` — use `var result []T` + `append` instead; CodeQL flags these patterns because the slice length/capacity is derived from user-controlled input, which can trigger incorrect memory allocation analysis
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The new guideline text attributes the CodeQL finding to “incorrect memory allocation analysis”, but the underlying concern is typically uncontrolled or potentially large allocations when sizing from untrusted/user-controlled input (DoS risk). Consider rewording to emphasize validating/capping untrusted sizes (and that preallocation is fine for trusted, bounded lengths) rather than a blanket prohibition, so the guidance doesn’t read like it’s just teaching people to evade CodeQL.

Suggested change
- Avoid `make([]T, len(input))` and `make([]T, 0, len(input))` — use `var result []T` + `append` instead; CodeQL flags these patterns because the slice length/capacity is derived from user-controlled input, which can trigger incorrect memory allocation analysis
- When sizing slices or maps from user-controlled or otherwise untrusted input, validate and cap the size before calling `make` to avoid uncontrolled allocations and potential DoS; preallocating with `make([]T, len(input))` or `make([]T, 0, len(input))` is fine when `len(input)` comes from trusted, bounded, or already-validated data

Copilot uses AI. Check for mistakes.
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.

[q] fix(functional-pragmatist): avoid make with len() to prevent CodeQL violations

3 participants