Как мне найти реализации типа функции? - PullRequest
0 голосов
/ 27 апреля 2018

В Go можно создавать типы функций (https://golang.org/ref/spec#Function_types), как это

type Printer func(s string)

Как мне найти все функции, которые удовлетворяют этому типу? Например, если у меня есть файл ниже, как я могу узнать, что consolePrinter это Printer?

package main

import "fmt"

func main() {
    printToScreen(consolePrinter)
}

// Printer defines a function that prints a string.
type Printer func(s string)

func printToScreen(p Printer) {
    p("Hello")
}

// consolePrinter is a Printer (the function signature is identical).
func consolePrinter(s string) {
    fmt.Println(s)
}

Я опробовал гуру, но функция implements, похоже, не поддерживает типы функций.

guru implements ./main.go:#134
/Users/adrian/go/gurutest/main.go:10.6-10.12: signature type Printer implements only interface{}

Ответы [ 4 ]

0 голосов
/ 29 апреля 2018

Рабочий пример, основанный на ответе Thundercat. guru потребуется сделать что-то подобное, чтобы обеспечить поддержку поиска подходящих функций для передачи (например, реализации http.HandleFunc).

 package main

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

 const hello = `package main

 import "fmt"

 const x = 1;

 func main() {
         fmt.Println("Hello, world")
 }

 // Printer defines a function that prints a string.
 type Printer func(s string)

 func consolePrinter(s string) {
    fmt.Println(s)
 }
 `

 func main() {
    fset := token.NewFileSet()

    // Parse the input string, []byte, or io.Reader,
    // recording position information in fset.
    // ParseFile returns an *ast.File, a syntax tree.
    f, err := parser.ParseFile(fset, "hello.go", hello, 0)
    if err != nil {
        log.Fatal(err) // parse error
    }

    // A Config controls various options of the type checker.
    // The defaults work fine except for one setting:
    // we must specify how to deal with imports.
    conf := types.Config{Importer: importer.Default()}

    // Type-check the package containing only file f.
    // Check returns a *types.Package.
    pkg, err := conf.Check("cmd/hello", fset, []*ast.File{f}, nil)
    if err != nil {
        log.Fatal(err) // type error
    }

    names, signatures := getFunctionTypes(pkg)
    for i, name := range names {
        fmt.Println("Functions which implement", name)
        for _, implementor := range getFunctionsWhichImplement(signatures[i], pkg) {
            fmt.Println(implementor)
        }
    }
 }

 func getFunctionTypes(pkg *types.Package) (names []string, signatures []*types.Signature) {
    for _, name := range pkg.Scope().Names() {
        o := pkg.Scope().Lookup(name)

        if _, isType := o.(*types.TypeName); !isType {
            continue
        }
        var sig *types.Signature
        var isFunc bool
        if sig, isFunc = o.Type().Underlying().(*types.Signature); !isFunc {
            continue
        }
        signatures = append(signatures, sig)
        names = append(names, name)
    }
    return
 }

 func getFunctionsWhichImplement(sig *types.Signature, pkg *types.Package) (fns []types.Object) {
    for _, name := range pkg.Scope().Names() {
        o := pkg.Scope().Lookup(name)
        if _, isType := o.(*types.TypeName); isType {
            continue
        }
        var csig *types.Signature
        var isFunc bool
        if csig, isFunc = o.Type().Underlying().(*types.Signature); !isFunc {
            continue
        }

        if types.AssignableTo(sig, csig) {
            fns = append(fns, o)
        }
    }
    return
 }

Вывод этого кода показан ниже:

 Functions which implement Printer
 func cmd/hello.consolePrinter(s string)
0 голосов
/ 27 апреля 2018

Я уверен, что есть более эффективные способы для конкретного случая печати, но в целом вам может быть лучше использовать более идиоматический подход к использованию интерфейсов.

Объявите интерфейс и типы, которые реализуют интерфейс:

type StringPrinter interface {
    PrintString(string)
}

type Console struct {
    // console specific stuff
}

func (c *Console) PrintString(s string) {
    // Printing code
}

type Paper struct {
    // paper specific stuff
}

func (p *Paper) PrintString(s string) {
    // Printing code
}

Затем для печати разными способами вы можете получить доступ через интерфейс общим способом:

func main() {
    var sp StringPrinter

    sp = &Console{ /* member inits */ }
    sp.PrintString("Hello on console") 

    sp = &Paper{ /* member inits */ }
    sp.PrintString("Hello on paper")
}

Вы сможете использовать гуру в этой форме кода для поиска объектов, которые реализуют интерфейс StringPrinter.

0 голосов
/ 27 апреля 2018

Вы ищете функции, которые назначаются на Printer.

Инструмент guru не поддерживает запрос «присваивается».

Вы можете написать программу для поиска функций, которые можно назначить для Printer, используя пакет go / types . Следуйте инструкции для ввода контрольного кода и используйте AssignableTo для поиска функций.

Если вас интересует только поиск функций, объявленных на уровне пакета в коде gofmted, тогда поиск вашего кода с помощью регулярного выражения может быть достаточно хорошим. Этот подход не точен, но прост во многих редакторах или в командной строке.

0 голосов
/ 27 апреля 2018

Здесь consolePrinter не относится к типу Printer, необходимо объявить consolePrinter как тип Printer. Вы видите, console.Printer - это функция с таким же базовым типом, поэтому ее можно передать printToScreen, что занимает Printer type. Посмотрите на разницу с их типами

package main

import (
    "fmt"
    "reflect"
)

func main() {
    printToScreen(consolePrinter)
    fmt.Println(reflect.TypeOf(consolePrinter)) // Prints func(string) as its type
}

// Printer defines a function that prints a string.
type Printer func(s string)

func printToScreen(p Printer) {
    var consoleprint Printer // declare to be of type Printer
    fmt.Println(reflect.TypeOf(consoleprint)) // Prints main.Printer
    p("Hello")
}

// consolePrinter is a Printer (the function signature is identical).
func consolePrinter(s string) {
    fmt.Println(s)
}

Пример игровой площадки

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...