Skip to content
Open
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions analysis/refdir/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func run(pass *analysis.Pass) (interface{}, error) {

if RefOrder[kind] == Ignore {
printer.Info(ref.Pos(), fmt.Sprintf("%s reference %s ignored by options", kind, ref.Name))
return
}

if pass.Fset.File(ref.Pos()).Name() != pass.Fset.File(def).Name() {
Expand Down Expand Up @@ -199,6 +200,16 @@ func run(pass *analysis.Pass) (interface{}, error) {

case *types.Func:
def = def.Origin()
// Allow direct self-recursion (call to the function we're inside).
if funcDecl != nil {
curr, ok := pass.TypesInfo.Defs[funcDecl.Name].(*types.Func)
if ok && curr != nil && curr.Origin() == def {
// For a recursive call, pass.TypesInfo.Uses[node] returns the current function’s object;
// comparing its Origin() to the current func’s Origin() lets us detect direct recursion even with generics instantiation.
break
}
}

if def.Parent() != nil && def.Parent() != def.Pkg().Scope() {
printer.Info(node.Pos(), fmt.Sprintf("skipping func ident %s with inner parent scope %s", node.Name, pass.Fset.Position(def.Parent().Pos())))
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package defaultdirs

// Direct recursion with a base case: function.
func RecursiveFunctionSafe(n int) int {
if n <= 0 {
return 0
}
return 1 + RecursiveFunctionSafe(n-1)
}

// Generic direct recursion with a base case: function.
func RecursiveGenericSafe[T any](n int, x T) int {
if n <= 0 {
return 0
}
return 1 + RecursiveGenericSafe[T](n-1, x)
}

// Mutual recursion: only this second call should be flagged (A is defined above).
func MutualA(n int) int {
if n <= 0 {
return 0
}
return MutualB(n - 1) // OK: call before MutualB's definition
}

func MutualB(n int) int {
if n <= 0 {
return 0
}
return MutualA(n - 1) // want "func reference MutualA is after definition"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package defaultdirs

// Value receiver recursion with a base case: method.
type FooValue struct{}

func (f FooValue) Bar(n int) int {
if n <= 0 {
return 0
}
return 1 + f.Bar(n-1)
}

// Pointer receiver recursion with a base case: method.
type FooPtr struct{}

func (f *FooPtr) Bar(n int) int {
if f == nil || n <= 0 {
return 0
}
return 1 + f.Bar(n-1)
}

// Generic receiver type recursion with a base case: method.
type Box[T any] struct{}

func (b Box[T]) Beat(n int) int {
if n <= 0 {
return 0
}
return 1 + b.Beat(n-1)
}

// Method value recursion with a base case: ensure we also don't flag taking method values.
type RecMethodValue struct{}

func (r RecMethodValue) MethodVal(n int) int {
f := r.MethodVal
if n <= 0 {
return 0
}
return 1 + f(n-1)
}