4

Is there a way to check if a public function/struct is used outside of the package in which it's declared? I'm not writing a public go module that's consumed anywhere else, and simply want to scan whether func Foo() it's used anywhere in my codebase outside of the package in which it's declared.

I'm using GoLand but any programmatic solution would do.

asaf92
  • 1,557
  • 1
  • 19
  • 30
  • GoLand should tell you just above the function, something like `4 usages` that's clickable to see the usages. You can also put your cursor of the symbol double-press Shift to bring up the search bar, then type in `Usages` and click whatever you want (Show Usages being the least intrusive imo). It should search your whole project. Just tested it myself on GoLand 2022.3.2 – jclasley Feb 06 '23 at 23:34

2 Answers2

2

Simplest solution: manually rename Foo() to Foo2(). Build/compile your project: if there are no compilation errors, it's not referenced in your code. Same check also works with any identifiers and with any IDEs (this doesn't use any of the IDE's features).

Obviously if you already have a Foo2 identifier, this will fail. But the idea is to rename it to a non-existing identifier...

icza
  • 389,944
  • 63
  • 907
  • 827
  • I can also do it with "find all references" and see if there are any usages outside of the package, but I'm looking for a programmatic way to achieve this (for example when a linter runs). – asaf92 Jan 22 '23 at 14:29
1

You can scan a particular package to see all the available function in it.

In this main.go, app the root package name and there is another package in database directory under the package name database. By running the code you will found all the function name available inside database package

package main

import (
    "fmt"
    "app/database"
    "go/ast"
    "go/parser"
    "go/token"
    "os"
)

// Name of the package you want to scan
const subPackage = "database"

func main() {

    set := token.NewFileSet()
    packs, err := parser.ParseDir(set, subPackage, nil, 0)
    if err != nil {
        fmt.Println("Failed to parse package:", err)
        os.Exit(1)
    }

    funcs := []*ast.FuncDecl{}
    for _, pack := range packs {
        for _, f := range pack.Files {
            for _, d := range f.Decls {
                if fn, isFn := d.(*ast.FuncDecl); isFn {
                    funcs = append(funcs, fn)
                }
            }
        }
    }
    fmt.Println("All the functions in the package:",subPackage)
    for _, fn := range funcs {
        fmt.Println(fn.Name.Name)
    }

    //  database Package is called/used
    database.Connection()
    

}

This will get all function declarations in the stated subpackage as an ast.FuncDecl. This isn't an invokable function; it's just a representation of its source code of it.

If you wanted to do anything like call these functions, you'd have to do something more sophisticated. After gathering these functions, you could gather them and output a separate file that calls each of them, then run the resulting file.