// Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package source_test import ( "go/token" "go/types" "testing" "golang.org/x/tools/go/packages" ) // This test reports any unexpected uses of (*go/token.File).Offset within // the gopls codebase to ensure that we don't check in more code that is prone // to panicking. All calls to (*go/token.File).Offset should be replaced with // calls to source.Offset. func TestTokenOffset(t *testing.T) { fset := token.NewFileSet() pkgs, err := packages.Load(&packages.Config{ Fset: fset, Mode: packages.NeedName | packages.NeedModule | packages.NeedCompiledGoFiles | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax | packages.NeedImports | packages.NeedDeps, }, "go/token", "golang.org/x/tools/internal/lsp/...", "golang.org/x/tools/gopls/...") if err != nil { t.Fatal(err) } var tokPkg *packages.Package for _, pkg := range pkgs { if pkg.PkgPath == "go/token" { tokPkg = pkg break } } typname, ok := tokPkg.Types.Scope().Lookup("File").(*types.TypeName) if !ok { t.Fatal("expected go/token.File typename, got none") } named, ok := typname.Type().(*types.Named) if !ok { t.Fatalf("expected named type, got %T", typname.Type) } var offset *types.Func for i := 0; i < named.NumMethods(); i++ { meth := named.Method(i) if meth.Name() == "Offset" { offset = meth break } } for _, pkg := range pkgs { for ident, obj := range pkg.TypesInfo.Uses { if ident.Name != "Offset" { continue } if pkg.PkgPath == "go/token" { continue } if !types.Identical(offset.Type(), obj.Type()) { continue } // The only permitted use is in golang.org/x/tools/internal/lsp/source.Offset, // so check the enclosing function. sourceOffset := pkg.Types.Scope().Lookup("Offset").(*types.Func) if sourceOffset.Pos() <= ident.Pos() && ident.Pos() <= sourceOffset.Scope().End() { continue // accepted usage } t.Errorf(`%s: Unexpected use of (*go/token.File).Offset. Please use golang.org/x/tools/internal/lsp/source.Offset instead.`, fset.Position(ident.Pos())) } } }