go/packages: enable UsesCgo in go/types

Add a new Mode bit, TypecheckCgo. When it is enabled, CompiledGoFiles
will contain cgo files before preprocessing, plus the cgo types file
generated by the cgo tool. Together, these can be type checked by
go/types with UsesCgo enabled.

Change-Id: I891a434f0193497d15e8c637910fb52b60455a06
Reviewed-on: https://go-review.googlesource.com/c/tools/+/229778
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Heschi Kreinick 2020-04-22 13:45:12 -04:00
parent 2bc93b1c0c
commit a4cde3673c
3 changed files with 87 additions and 1 deletions

View File

@ -544,6 +544,25 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
module: p.Module,
}
if (state.cfg.Mode&TypecheckCgo) != 0 && len(p.CgoFiles) != 0 {
if len(p.CompiledGoFiles) > len(p.GoFiles) {
// We need the cgo definitions, which are in the first
// CompiledGoFile after the non-cgo ones. This is a hack but there
// isn't currently a better way to find it. We also need the pure
// Go files and unprocessed cgo files, all of which are already
// in pkg.GoFiles.
cgoTypes := p.CompiledGoFiles[len(p.GoFiles)]
pkg.CompiledGoFiles = append([]string{cgoTypes}, pkg.GoFiles...)
} else {
// golang/go#38990: go list silently fails to do cgo processing
pkg.CompiledGoFiles = nil
pkg.Errors = append(pkg.Errors, Error{
Msg: "go list failed to return CompiledGoFiles; https://golang.org/issue/38990?",
Kind: ListError,
})
}
}
// Work around https://golang.org/issue/28749:
// cmd/go puts assembly, C, and C++ files in CompiledGoFiles.
// Filter out any elements of CompiledGoFiles that are also in OtherFiles.

View File

@ -19,6 +19,7 @@ import (
"log"
"os"
"path/filepath"
"reflect"
"strings"
"sync"
@ -49,6 +50,10 @@ const (
// NeedCompiledGoFiles adds CompiledGoFiles.
NeedCompiledGoFiles
// TypecheckCgo enables full support for type checking cgo. Requires Go 1.15+.
// Has no effect without NeedTypes.
TypecheckCgo
// NeedImports adds Imports. If NeedDeps is not set, the Imports field will contain
// "placeholder" Packages with only the ID set.
NeedImports
@ -257,7 +262,7 @@ type Package struct {
GoFiles []string
// CompiledGoFiles lists the absolute file paths of the package's source
// files that were presented to the compiler.
// files that are suitable for type checking.
// This may differ from GoFiles if files are processed before compilation.
CompiledGoFiles []string
@ -878,6 +883,19 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
Error: appendError,
Sizes: ld.sizes,
}
if (ld.Mode & TypecheckCgo) != 0 {
// TODO: remove this when we stop supporting 1.14.
rtc := reflect.ValueOf(tc).Elem()
usesCgo := rtc.FieldByName("UsesCgo")
if !usesCgo.IsValid() {
appendError(Error{
Msg: "TypecheckCgo requires Go 1.15+",
Kind: ListError,
})
return
}
usesCgo.SetBool(true)
}
types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
lpkg.importErrors = nil // no longer needed

View File

@ -7,6 +7,7 @@
package packages_test
import (
"strings"
"testing"
"golang.org/x/tools/go/packages"
@ -41,3 +42,51 @@ func testInvalidFilesInXTest(t *testing.T, exporter packagestest.Exporter) {
t.Errorf("expected 3 packages, got %d", len(initial))
}
}
func TestTypecheckCgo(t *testing.T) {
packagestest.TestAll(t, testTypecheckCgo)
}
func testTypecheckCgo(t *testing.T, exporter packagestest.Exporter) {
// The android builders have a complex setup which causes this test to fail. See discussion on
// golang.org/cl/214943 for more details.
if !hasGoBuild() {
t.Skip("this test can't run on platforms without go build. See discussion on golang.org/cl/214943 for more details.")
}
const cgo = `package cgo
import "C"
func Example() {
C.CString("hi")
}
`
exported := packagestest.Export(t, exporter, []packagestest.Module{
{
Name: "golang.org/fake",
Files: map[string]interface{}{
"cgo/cgo.go": cgo,
},
},
})
defer exported.Cleanup()
exported.Config.Mode = packages.NeedFiles | packages.NeedCompiledGoFiles |
packages.NeedSyntax | packages.NeedDeps | packages.NeedTypes |
packages.TypecheckCgo
initial, err := packages.Load(exported.Config, "golang.org/fake/cgo")
if err != nil {
t.Fatal(err)
}
pkg := initial[0]
if len(pkg.Errors) != 0 {
t.Fatalf("package has errors: %v", pkg.Errors)
}
expos := pkg.Types.Scope().Lookup("Example").Pos()
fname := pkg.Fset.File(expos).Name()
if !strings.HasSuffix(fname, "cgo.go") {
t.Errorf("position for cgo package was loaded from %v, wanted cgo.go", fname)
}
}