Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: replaces gcexportdata.NewImporter #716

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions lint/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package lint
import (
"bytes"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"go/types"
Expand All @@ -14,20 +13,23 @@ import (

// File abstraction used for representing files.
type File struct {
Name string
Pkg *Package
content []byte
AST *ast.File
Name string
Pkg *Package
AST *ast.File
}

// IsTest returns if the file contains tests.
func (f *File) IsTest() bool { return strings.HasSuffix(f.Name, "_test.go") }

// Content returns the file's content.
func (f *File) Content() []byte {
return f.content
buf := bytes.Buffer{}
fs := token.NewFileSet()
printer.Fprint(&buf, fs, f.AST)
return buf.Bytes()
}

/*
// NewFile creates a new file
func NewFile(name string, content []byte, pkg *Package) (*File, error) {
f, err := parser.ParseFile(pkg.fset, name, content, parser.ParseComments)
Expand All @@ -41,24 +43,25 @@ func NewFile(name string, content []byte, pkg *Package) (*File, error) {
AST: f,
}, nil
}
*/

// ToPosition returns line and column for given position.
func (f *File) ToPosition(pos token.Pos) token.Position {
return f.Pkg.fset.Position(pos)
return f.Pkg.goPkg.Fset.Position(pos)
}

// Render renders a node.
func (f *File) Render(x interface{}) string {
var buf bytes.Buffer
if err := printer.Fprint(&buf, f.Pkg.fset, x); err != nil {
if err := printer.Fprint(&buf, f.Pkg.goPkg.Fset, x); err != nil {
panic(err)
}
return buf.String()
}

// CommentMap builds a comment map for the file.
func (f *File) CommentMap() ast.CommentMap {
return ast.NewCommentMap(f.Pkg.fset, f.AST, f.AST.Comments)
return ast.NewCommentMap(f.Pkg.goPkg.Fset, f.AST, f.AST.Comments)
}

var basicTypeKinds = map[types.BasicKind]string{
Expand All @@ -77,7 +80,7 @@ func (f *File) IsUntypedConst(expr ast.Expr) (defType string, ok bool) {
// Re-evaluate expr outside its context to see if it's untyped.
// (An expr evaluated within, for example, an assignment context will get the type of the LHS.)
exprStr := f.Render(expr)
tv, err := types.Eval(f.Pkg.fset, f.Pkg.TypesPkg(), expr.Pos(), exprStr)
tv, err := types.Eval(f.Pkg.goPkg.Fset, f.Pkg.goPkg.Types, expr.Pos(), exprStr)
if err != nil {
return "", false
}
Expand Down
69 changes: 34 additions & 35 deletions lint/linter.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package lint

import (
"bufio"
"bytes"
"fmt"
"go/token"
"os"
"regexp"
"strconv"
"strings"
"sync"

"golang.org/x/tools/go/packages"
)

// ReadFile defines an abstraction for reading files.
Expand Down Expand Up @@ -49,18 +50,18 @@ func (l Linter) readFile(path string) (result []byte, err error) {
}

var (
genHdr = []byte("// Code generated ")
genFtr = []byte(" DO NOT EDIT.")
genHdr = "// Code generated "
genFtr = " DO NOT EDIT."
)

// Lint lints a set of files with the specified rule.
func (l *Linter) Lint(packages [][]string, ruleSet []Rule, config Config) (<-chan Failure, error) {
func (l *Linter) Lint(pckgs []*packages.Package, ruleSet []Rule, config Config) (<-chan Failure, error) {
failures := make(chan Failure)

var wg sync.WaitGroup
for _, pkg := range packages {
for _, pkg := range pckgs {
wg.Add(1)
go func(pkg []string) {
go func(pkg *packages.Package) {
if err := l.lintPackage(pkg, ruleSet, config, failures); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
Expand All @@ -77,49 +78,47 @@ func (l *Linter) Lint(packages [][]string, ruleSet []Rule, config Config) (<-cha
return failures, nil
}

func (l *Linter) lintPackage(filenames []string, ruleSet []Rule, config Config, failures chan Failure) error {
pkg := &Package{
fset: token.NewFileSet(),
files: map[string]*File{},
}
for _, filename := range filenames {
content, err := l.readFile(filename)
if err != nil {
return err
}
if !config.IgnoreGeneratedHeader && isGenerated(content) {
continue
}
func (l *Linter) lintPackage(goPkg *packages.Package, ruleSet []Rule, config Config, failures chan Failure) error {
lintPkg := NewPackage(goPkg)
for _, file := range goPkg.Syntax {
/*
content, err := l.readFile(filename)
if err != nil {
return err
}*/

file, err := NewFile(filename, content, pkg)
if err != nil {
addInvalidFileFailure(filename, err.Error(), failures)
if !config.IgnoreGeneratedHeader && isGenerated(file.Doc.Text()) {
continue
}
pkg.files[filename] = file
/*
file, err := NewFile(filename, content, pkg)
if err != nil {
addInvalidFileFailure(filename, err.Error(), failures)
continue
}
*/
newFile := File{
Name: file.Name.Name,
Pkg: &lintPkg,
AST: file,
}
lintPkg.files[file.Name.String()] = &newFile
}

if len(pkg.files) == 0 {
if len(lintPkg.files) == 0 {
return nil
}

pkg.lint(ruleSet, config, failures)
lintPkg.lint(ruleSet, config, failures)

return nil
}

// isGenerated reports whether the source file is generated code
// according the rules from https://golang.org/s/generatedcode.
// This is inherited from the original go lint.
func isGenerated(src []byte) bool {
sc := bufio.NewScanner(bytes.NewReader(src))
for sc.Scan() {
b := sc.Bytes()
if bytes.HasPrefix(b, genHdr) && bytes.HasSuffix(b, genFtr) && len(b) >= len(genHdr)+len(genFtr) {
return true
}
}
return false
func isGenerated(fileComment string) bool {
return strings.HasPrefix(fileComment, genHdr) && strings.HasSuffix(fileComment, genFtr) && len(fileComment) >= len(genHdr)+len(genFtr)
}

// addInvalidFileFailure adds a failure for an invalid formatted file
Expand Down
88 changes: 49 additions & 39 deletions lint/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,31 @@ import (
"sync"

"golang.org/x/tools/go/gcexportdata"
"golang.org/x/tools/go/packages"

"github.com/mgechev/revive/internal/typeparams"
)

// Package represents a package in the project.
type Package struct {
fset *token.FileSet
goPkg *packages.Package
files map[string]*File

typesPkg *types.Package
typesInfo *types.Info

// sortable is the set of types in the package that implement sort.Interface.
sortable map[string]bool
// main is whether this is a "main" package.
main int
sync.RWMutex
}

func NewPackage(pkg *packages.Package) Package {
return Package{
goPkg: pkg,
files: map[string]*File{},
sortable: map[string]bool{},
}
}

var newImporter = func(fset *token.FileSet) types.ImporterFrom {
return gcexportdata.NewImporter(fset, make(map[string]*types.Package))
}
Expand Down Expand Up @@ -65,14 +71,14 @@ func (p *Package) IsMain() bool {
func (p *Package) TypesPkg() *types.Package {
p.RLock()
defer p.RUnlock()
return p.typesPkg
return p.goPkg.Types
}

// TypesInfo yields type information of this package identifiers
func (p *Package) TypesInfo() *types.Info {
p.RLock()
defer p.RUnlock()
return p.typesInfo
return p.goPkg.TypesInfo
}

// Sortable yields a map of sortable types in this package
Expand All @@ -83,41 +89,45 @@ func (p *Package) Sortable() map[string]bool {
}

// TypeCheck performs type checking for given package.
func (p *Package) TypeCheck() error {
p.Lock()
defer p.Unlock()

// If type checking has already been performed
// skip it.
if p.typesInfo != nil || p.typesPkg != nil {
return nil
}
config := &types.Config{
// By setting a no-op error reporter, the type checker does as much work as possible.
Error: func(error) {},
Importer: newImporter(p.fset),
}
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Scopes: make(map[ast.Node]*types.Scope),
}
var anyFile *File
var astFiles []*ast.File
for _, f := range p.files {
anyFile = f
astFiles = append(astFiles, f.AST)
}
func (p *Package) TypeCheck() error {
return nil // TODO delete this function
/*
p.Lock()
defer p.Unlock()

// If type checking has already been performed
// skip it.
if p.typesInfo != nil || p.typesPkg != nil {
return nil
}
config := &types.Config{
// By setting a no-op error reporter, the type checker does as much work as possible.
Error: func(error) {},
Importer: newImporter(p.fset),
}
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Scopes: make(map[ast.Node]*types.Scope),
}
var anyFile *File
var astFiles []*ast.File
for _, f := range p.files {
anyFile = f
astFiles = append(astFiles, f.AST)
}

typesPkg, err := check(config, anyFile.AST.Name.Name, p.fset, astFiles, info)
typesPkg, err := check(config, anyFile.AST.Name.Name, p.fset, astFiles, info)

// Remember the typechecking info, even if config.Check failed,
// since we will get partial information.
p.typesPkg = typesPkg
p.typesInfo = info
// Remember the typechecking info, even if config.Check failed,
// since we will get partial information.
p.typesPkg = typesPkg
p.typesInfo = info

return err
return err
*/
}

// check function encapsulates the call to go/types.Config.Check method and
Expand All @@ -136,10 +146,10 @@ func check(config *types.Config, n string, fset *token.FileSet, astFiles []*ast.

// TypeOf returns the type of an expression.
func (p *Package) TypeOf(expr ast.Expr) types.Type {
if p.typesInfo == nil {
if p.goPkg.TypesInfo == nil {
return nil
}
return p.typesInfo.TypeOf(expr)
return p.goPkg.TypesInfo.TypeOf(expr)
}

type walker struct {
Expand Down
24 changes: 23 additions & 1 deletion revivelib/core.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package revivelib

import (
"flag"
"io/ioutil"
"log"
"strings"

"github.com/mgechev/dots"
"golang.org/x/tools/go/packages"

"github.com/mgechev/revive/config"
"github.com/mgechev/revive/lint"
"github.com/mgechev/revive/logging"
Expand Down Expand Up @@ -162,6 +164,7 @@ func (r *Revive) Format(
return output, exitCode, nil
}

/*
func getPackages(includePatterns []string, excludePatterns ArrayFlags) ([][]string, error) {
globs := normalizeSplit(includePatterns)
if len(globs) == 0 {
Expand All @@ -175,6 +178,25 @@ func getPackages(includePatterns []string, excludePatterns ArrayFlags) ([][]stri

return packages, nil
}
*/

func getPackages(includePatterns []string, excludePatterns ArrayFlags) ([]*packages.Package, error) {
globs := normalizeSplit(flag.Args())
if len(globs) == 0 {
globs = append(globs, ".")
}

cfg := &packages.Config{Mode: packages.LoadSyntax}
pckgs, err := packages.Load(cfg, globs...)
if err != nil {
return nil, errors.Wrap(err, "loading packages")
}
if packages.PrintErrors(pckgs) > 0 {
return nil, errors.Wrap(err, "loading packages")
}

return pckgs, nil
}

func normalizeSplit(strs []string) []string {
res := []string{}
Expand Down
2 changes: 1 addition & 1 deletion rule/time-equal.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (*TimeEqualRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {

w := &lintTimeEqual{file, onFailure}
if w.file.Pkg.TypeCheck() != nil {
return nil
panic(1)
}

ast.Walk(w, file.AST)
Expand Down
Loading