Могу ли я узнать, происходит ли идентификатор из стандартной библиотеки на основе AST Go? - PullRequest
0 голосов
/ 06 января 2020

Я делаю линтер, и часть его хочет узнать, исходит ли идентификатор из стандартной библиотеки или нет.

В выражении селектора есть два поля " X "и" Sel ". Для селектора «io.Reader» и «X», и «Sel» будут иметь тип «* ast.Ident», где «X» будет представлять «io», а «Sel» будет представлять «Reader».

Как мне проверить, происходит ли "* ast.Ident" для "io" из стандартной библиотеки или другого пакета?

Я не уверен, что AST содержит достаточно информации, чтобы ответить на вопрос. Если это так. Что бы вы предложили, это самый простой способ получить правильные результаты?

1 Ответ

0 голосов
/ 06 января 2020

Используйте ast.NewPackage для разрешения идентификаторов в AST. Для функции NewPackage требуется средство импорта, которое ищет информацию о пакете для пути импорта. Вот простой импортер, который отвечает потребностям этого вопроса. Импортер возвращает объект пакета с правильным именем для стандартных пакетов и сгенерированным именем для нестандартных пакетов.

var genNameCounter int

func simpleImporter(imports map[string]*ast.Object, p string) (*ast.Object, error) {
    pkg := imports[p]
    if pkg != nil {
        return pkg, nil
    }

    // This is correct for standard packages
    name := path.Base(p)

    if !isStandardLibraryPath(p) {
        // Generate fake name that will not match name of a standard package.
        genNameCounter++
        name = fmt.Sprintf("genName%d", genNameCounter)
    }

    pkg = ast.NewObj(ast.Pkg, name)
    pkg.Data = ast.NewScope(nil)
    imports[p] = pkg
    return pkg, nil
}

Импортер использует функцию, которая возвращает true для путей импорта в стандартной библиотеке. Приближение этой функции следует. См. Могу ли я перечислить все стандартные Go пакеты? для получения информации о том, как реализовать реальную функцию.

func isStandardLibraryPath(path string) bool {
    return !strings.Contains(path, ".")
}

Если вы хотите разрешить все идентификаторы пакетов, используйте go / build package, чтобы найти имя пакета для пути импорта. Преимущество использования simpleImporter, представленного здесь, заключается в том, что функция не читает исходные файлы на диске.

Разрешите идентификаторы и создайте карту из областей пакета для пути импорта:

pkg, err := ast.NewPackage(fset, files, simpleImporter, nil)

// Create map from scope to path
paths := make(map[interface{}]string)
for path, obj := range pkg.Imports {
    paths[obj.Data] = path
}

Используйте эту карту, чтобы определить, разрешен ли *ast.Ident в стандартный пакет.

func isStandardLibraryIdent(paths map[interface{}]string, ident *ast.Ident) bool {
    if ident.Obj == nil || ident.Obj.Kind != ast.Pkg {
        return false
    }
    path, ok := paths[ident.Obj.Data]
    if !ok {
        return false
    }
    return isStandardLibraryPath(path)
}

Запустите его на игровой площадке Go .

...