Search code examples
goanalysis

How to get type from *types.Named


I'm trying to find function calls of funcs that have context.Context as first parameter.

I've been able to do what's shown below but I'm stuck at getting the underlying type from *types.Named. How can I do this?

package main

import (
    "bytes"
    "context"
    "fmt"
    "go/ast"
    "go/printer"
    "go/token"
    "go/types"

    "golang.org/x/tools/go/analysis"
    "golang.org/x/tools/go/analysis/singlechecker"
)

var Analyzer = &analysis.Analyzer{
    Name: "addlint",
    Doc:  "reports integer additions",
    Run:  run,
}

func main() {
    singlechecker.Main(Analyzer)
}

func funcHasContextContextAsFirstParam(pass *analysis.Pass, expr ast.Expr) bool {
    t := pass.TypesInfo.TypeOf(expr)
    if t == nil {
        return false
    }

    bt, ok := t.Underlying().(*types.Signature)
    if !ok {
        return false
    }

    fmt.Printf("signature: %+v - %T\n", bt, bt)

    params := bt.Params()
    for i := 0; i < params.Len(); i++ {
        v := params.At(i)
        fmt.Printf("Type :  %T\n", v.Type())

        if named, ok := v.Type().(*types.Named); ok {
            // fmt.Printf("named : %v - %T\n", named.Obj(), named.Obj())
            fmt.Printf("named : %T\n", named)
            fmt.Printf("named.Obj() : %T\n", named.Obj())

            typ := named.Underlying()
            fmt.Printf("typ:  %T\n", typ.Underlying())

            if _, ok = typ.(context.Context); ok {
                fmt.Printf("context.Context type!\n")
            }
        }
    }
    return true
}

func run(pass *analysis.Pass) (interface{}, error) {
    for _, file := range pass.Files {
        ast.Inspect(file, func(n ast.Node) bool {
            be, ok := n.(*ast.CallExpr)
            if !ok {
                return true
            }

            fmt.Printf("call expression %+v\n", be)
            funcHasContextContextAsFirstParam(pass, be.Fun)

            return true
        })
    }

    return nil, nil
}

That's the output I'm getting:

call expression &{Fun:foo Lparen:6160580 Args:[c 0xc0003c5780 0xc0003c57c0] Ellipsis:0 Rparen:6160596}
signature: func(ctx context.Context, n int, str string) - *types.Signature
Type :  *types.Named
named : *types.Named
named.Obj() : *types.TypeName
typ:  *types.Interface

Solution

  • I've ended up with something like this:

    func funcHasContextContextAsFirstParam(pass *analysis.Pass, expr ast.Expr) bool {
        t := pass.TypesInfo.TypeOf(expr)
        if t == nil {
            return false
        }
    
        bt, ok := t.Underlying().(*types.Signature)
        if !ok {
            return false
        }
    
        params := bt.Params()
    
        if params.Len() < 1 {
            return false
        }
    
        param := params.At(0)
        named, ok := param.Type().(*types.Named)
        if !ok {
            return false
        }
    
        namedObj := named.Obj()
        if namedObj.Name() != "Context" || namedObj.Pkg().Name() != "context" {
            return false
        }
    
        return true
    }