Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 12 additions & 13 deletions .github/workflows/functional-pragmatist.md
Original file line number Diff line number Diff line change
Expand Up @@ -458,17 +458,18 @@ func TestFilter(t *testing.T) {
package fp

// Map transforms each element in a slice
// 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))
Comment on lines +461 to +465

Copilot AI Mar 31, 2026

Copy link

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.
}
return result
}

// Filter returns elements that match the predicate
func Filter[T any](slice []T, fn func(T) bool) []T {
result := make([]T, 0, len(slice))
var result []T
for _, v := range slice {
if fn(v) {
result = append(result, v)
Expand Down Expand Up @@ -507,10 +508,10 @@ for _, name := range names {
filters = append(filters, Filter{Name: name})
}

// After: Immutable initialization
filters := make([]Filter, len(names))
for i, name := range names {
filters[i] = Filter{Name: name}
// After: Immutable initialization using append
var filters []Filter
for _, name := range names {
filters = append(filters, Filter{Name: name})
}
Comment on lines +511 to 515

Copilot AI Mar 31, 2026

Copy link

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.
// Or even better if simple:
filters := sliceutil.Map(names, func(name string) Filter {
Expand Down Expand Up @@ -570,7 +571,7 @@ activeItems := sliceutil.Filter(items, func(item Item) bool { return item.Active
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 {
Comment on lines 571 to 575

Copilot AI Mar 31, 2026

Copy link

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.
if item.Active {
activeNames = append(activeNames, item.Name)
Expand Down Expand Up @@ -1339,7 +1340,7 @@ func NewService(config *Config, cache *Cache) *Service
- Go doesn't have built-in map/filter/reduce - that's okay!
- 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

Copilot AI Mar 31, 2026

Copy link

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.
- Simple for-loops are idiomatic Go - don't force functional style
- Functional options is a well-established Go pattern - use it confidently
- Pure functions align well with Go's simplicity philosophy
Expand Down Expand Up @@ -1387,9 +1388,7 @@ func NewConfig(host string, port int) (*Config, error) {
```go
// Return copy to prevent mutation of internal state
func (s *Service) GetItems() []Item {
result := make([]Item, len(s.items))
copy(result, s.items)
return result
return slices.Clone(s.items)
}
```

Expand Down
Loading