Skip to content

QF1008: False positive when overriding a method on deeply embedded struct #1682

@whs

Description

@whs

Code:

package main

import "fmt"

type Embedded2 struct {
	Output *string
}

func (e Embedded2) String() string {
	return *e.Output
}

type Embedded struct {
	Embedded2
}

type Wrapper struct {
	Embedded
}

func (w Wrapper) String() string {
	if w.Embedded.Embedded2.Output != nil {
		return w.Embedded.Embedded2.String()
	}
	return ""
}

func main() {
	s := "test"
	w := Wrapper{
		Embedded{
			Embedded2{
				Output: &s,
			},
		},
	}
	fmt.Println(w.String())
}

Result:

test

Result of golangci-lint:

cmd/testgci/testgci.go:22:7: QF1008: could remove embedded field "Embedded" from selector (staticcheck)
        if w.Embedded.Embedded2.Output != nil {
             ^
cmd/testgci/testgci.go:23:12: QF1008: could remove embedded field "Embedded" from selector (staticcheck)
                return w.Embedded.Embedded2.String()
                         ^
2 issues:
* staticcheck: 2

After running golangci-lint --fix:

package main

import "fmt"

type Embedded2 struct {
	Output *string
}

func (e Embedded2) String() string {
	return *e.Output
}

type Embedded struct {
	Embedded2
}

type Wrapper struct {
	Embedded
}

func (w Wrapper) String() string {
	if w.Output != nil {
		return w.String()
	}
	return ""
}

func main() {
	s := "test"
	w := Wrapper{
		Embedded{
			Embedded2{
				Output: &s,
			},
		},
	}
	fmt.Println(w.String())
}

Output:

runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc020160388 stack=[0xc020160000, 0xc040160000]
fatal error: stack overflow

runtime stack:
runtime.throw({0x4bdc1a?, 0x413045?})
        /nix/store/3fd683jfggglpshxprz9mi5sz8wd3c9p-go-1.25.0/share/go/src/runtime/panic.go:1094 +0x48 fp=0x7fff7af4aa38 sp=0x7fff7af4aa08 pc=0x46dc08
runtime.newstack()
        /nix/store/3fd683jfggglpshxprz9mi5sz8wd3c9p-go-1.25.0/share/go/src/runtime/stack.go:1159 +0x5bd fp=0x7fff7af4ab68 sp=0x7fff7af4aa38 pc=0x455b9d
runtime.morestack()
        /nix/store/3fd683jfggglpshxprz9mi5sz8wd3c9p-go-1.25.0/share/go/src/runtime/asm_amd64.s:620 +0x7d fp=0x7fff7af4ab70 sp=0x7fff7af4ab68 pc=0x47221d

goroutine 1 gp=0xc000002380 m=0 mp=0x571be0 [running]:
main.Wrapper.String({{{0xc04015ff20?}}})
        /src/cmd/testgci/testgci.go:21 +0x32 fp=0xc020160398 sp=0xc020160390 pc=0x498132
main.Wrapper.String(...)
        /src/cmd/testgci/testgci.go:23
main.Wrapper.String({{{0x0?}}})
        /src/cmd/testgci/testgci.go:23 +0x18 fp=0xc0201603b0 sp=0xc020160398 pc=0x498118

Expected result: It should keep return w.Embedded.Embedded2.String()

I've tried without Embedded2 (inline Embedded2 into Embedded), which the refactor correctly change the return to return w.Embedded.String().

Version: golangci-lint has version v2.4.0-custom-gcl-47DEQpj8HBSaTImW5JCeuQeRkm5NMpJWZG3hSuFU built with go1.25.0 from ? on 2025-10-08 08:56:29.481973397 +0000 UTC

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions