0

I'm trying to static analyze Go files. For this I need to parse variables of the following format:

shape.color = color.red

I need to find all variables accessed with dot notation. For example, I need to know that shape variable has the color attribute. And also need that color variable has red attribute. Im trying to use the go/ast and go/parser package but cant figure out a way to do it.

N.B. If it's something like shape.color() i.e, a method, then it shouldn't be counted

3
  • 1
    Can you show the code you've tried to do this and explain what issues specifically you've had with it? Commented Oct 25, 2018 at 17:57
  • You might have a good reason, but my first question is: Why are you reinventing the wheel? Why not use the standard Go parsing libraries from the Go project? Commented Oct 26, 2018 at 8:08
  • @Flimzy I was trying, but I couldn't. I was finally able to. I posted the answer below. Commented Oct 26, 2018 at 8:19

3 Answers 3

2

Ah! The following code prints all the variables accessed with dot notation!

package main

import (
    "fmt"
    "go/ast"
    "go/parser"
    "go/token"
    "log"
)

func main() {
    v := visitor{}
    filename := "test.go"

    fset := token.NewFileSet()
    f, err := parser.ParseFile(fset, filename, nil, 0)

    if err != nil {
        log.Fatal(err)
    }

    ast.Walk(&v, f)
}

type visitor struct {
}

func (v *visitor) Visit(n ast.Node) ast.Visitor {
    if n == nil {
        return v
    }

    if selectorExp, ok := n.(*ast.SelectorExpr); ok {
        if x, ok := selectorExp.X.(*ast.Ident); ok {
            if x.Obj == nil {
                return v
            }  

            fmt.Printf("%s.%s\n", x.Name, selectorExp.Sel.Name)
        }
    }
    return v
}
Sign up to request clarification or add additional context in comments.

Comments

1

It looks like you're trying to create your own AST, as the right side of the expression you've given doesn't seem like a variable, otherwise I assume it as a struct. However, that's doesn't make sense too, since it'd be literally illogical to put a field named red in a struct named color. It also seems like you're trying to access a variable of a package but that also wouldn't work because lowercased first letter means that that the entity is unexported.

Leaving all them aside, I wrote a little snippet just to abide by the conditions you've listed.

  • shape variable has the color attribute.
  • color variable has red attribute.

https://play.golang.org/p/gIpctQ1XSgT, I adapted it just for a single line and panicked whenever conditions aren't met for brevity. Feel free to adjust it on your needs.

package main

import (
    "go/ast"
    "go/format"
    "go/parser"
    "go/token"
    "os"
)

func main() {
    expr, err := parser.ParseExpr("shape.color==color.red")
    if err != nil {
        panic(err)
    }

    // Checking if the expression was binary.
    bExpr, ok := expr.(*ast.BinaryExpr)
    if !ok {
        panic("expr is not a binary expr.")
    }

    // If the operation is not “==”, die.
    if bExpr.Op != token.EQL {
        panic("the op should have been ==.")
    }

    // Left must be a selector expr, meaning followed with a selector which is “dot” in this case.
    left, ok := bExpr.X.(*ast.SelectorExpr)
    if !ok {
        panic("left should have been a selector expr.")
    }

    // Same as above.
    right, ok := bExpr.Y.(*ast.SelectorExpr)
    if !ok {
        panic("right should have been a selector expr.")
    }

    // Checking for attributes.
    if left.Sel.Name != "color" {
        panic("left should have had a color attr.")
    }

    // Same as above.
    if right.Sel.Name != "red" {
        panic("right should have had a red attr.")
    }

    // Then we finally gofmt the code and print it to stdout.
    if err := format.Node(os.Stdout, token.NewFileSet(), expr); err != nil {
        panic(err)
    }
}

Comments

0

If you are comparing two go variables that you do not know in advance you will need to use reflection. This will allow you to reflectively compare the two fields:

type color struct {
    Red string
}

type shape struct {
    Color string
}

func main() {
    color := color{Red: "red"}
    shape := shape{Color: "red"}

    colorVal := reflect.ValueOf(color)
    shapeVal := reflect.ValueOf(shape)

    colorRedField := colorVal.FieldByName("Red")
    shapeColorField := shapeVal.FieldByName("Color")

    fmt.Println(colorRedField)
    fmt.Println(shapeColorField)
    fmt.Println(colorRedField.Interface() == shapeColorField.Interface())
}

https://play.golang.org/p/gvTJYwStP1O

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.