Finding scope level using Golang Analysis package

61 views Asked by At

I am having trouble finding scope level using the Go Analyze package.

Given an example.go

package main

import "log"

var myglobalvar = "hello from global"

func myLog(format string, args ...interface{}) {
    const prefix = "[my] "
    mylocalvar := "hello from local"
    log.Printf(prefix+myglobalvar+mylocalvar+format, args...)
}

func main() {
    mystring := "HELLO FROM MAIN"
    myLog(mystring)
    myLog(myglobalvar)
}

I'm trying to find the scope level on the arguments of a called function to determine if it is a global var or local var. I'm currently stuck at the argument's object decl and not sure how to proceed from here. In the end I want to build a custom linter for golangci-lint.

How can I find the argument's scope?

I have tried using the recommended golang analyze package.
My analyze.go:

func run(pass *analysis.Pass) (interface{}, error) {
    inspect := func(node ast.Node) bool {
                // find all callexprs
        callExpr, ok := node.(*ast.CallExpr)
        if !ok {
            return true
        }

                // find function identity
        funcIdent, ok := callExpr.Fun.(*ast.Ident)
        if !ok {
            return true
        }

                // if function name matches 
        if funcIdent.Name == "myLog" {
            funcArgs := callExpr.Args

            for _, s := range funcArgs {
                argObject, ok := s.(*ast.Ident)
                if !ok {
                    return true
                }
                declObj, ok := argObject.Obj.Decl.(*ast.Ident)
                if !ok {
                    return true
                }
                fmt.Println(declObj) // I am stuck here
            }
        }

        return true
    }

    for _, f := range pass.Files {
        ast.Inspect(f, inspect)
    }
    return nil, nil
}

EDIT: After a deeper dive I have discovered that once the object.Decl is created, it is assignStmt if it is a local scope and valueSpec if it's global.

 if obj != nil {
   if obj.Kind == ast.Var {
     fmt.Println("V: ")
     switch v := obj.Decl.(type) {
     case *ast.AssignStmt:
        fmt.Println("assignstmt")
        fmt.Println("local assignment")
     case *ast.ValueSpec:
        fmt.Println("valuespec")
        fmt.Println("global assignment")
     default:
        fmt.Printf("No idea: %s.\n", v)
   }
}
0

There are 0 answers