140 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			140 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package golinters
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"go/ast"
 | |
| 	"go/token"
 | |
| 
 | |
| 	"github.com/golangci/golangci-lint/pkg/lint/linter"
 | |
| 	"github.com/golangci/golangci-lint/pkg/result"
 | |
| )
 | |
| 
 | |
| type Scopelint struct{}
 | |
| 
 | |
| func (Scopelint) Name() string {
 | |
| 	return "scopelint"
 | |
| }
 | |
| 
 | |
| func (Scopelint) Desc() string {
 | |
| 	return "Scopelint checks for unpinned variables in go programs"
 | |
| }
 | |
| 
 | |
| func (lint Scopelint) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
 | |
| 	var res []result.Issue
 | |
| 
 | |
| 	for _, f := range lintCtx.ASTCache.GetAllValidFiles() {
 | |
| 		n := Node{
 | |
| 			fset:          f.Fset,
 | |
| 			dangerObjects: map[*ast.Object]struct{}{},
 | |
| 			unsafeObjects: map[*ast.Object]struct{}{},
 | |
| 			skipFuncs:     map[*ast.FuncLit]struct{}{},
 | |
| 			issues:        &res,
 | |
| 		}
 | |
| 		ast.Walk(&n, f.F)
 | |
| 	}
 | |
| 
 | |
| 	return res, nil
 | |
| }
 | |
| 
 | |
| // The code below is copy-pasted from https://github.com/kyoh86/scopelint
 | |
| 
 | |
| // Node represents a Node being linted.
 | |
| type Node struct {
 | |
| 	fset          *token.FileSet
 | |
| 	dangerObjects map[*ast.Object]struct{}
 | |
| 	unsafeObjects map[*ast.Object]struct{}
 | |
| 	skipFuncs     map[*ast.FuncLit]struct{}
 | |
| 	issues        *[]result.Issue
 | |
| }
 | |
| 
 | |
| // Visit method is invoked for each node encountered by Walk.
 | |
| // If the result visitor w is not nil, Walk visits each of the children
 | |
| // of node with the visitor w, followed by a call of w.Visit(nil).
 | |
| //nolint:gocyclo,gocritic
 | |
| func (f *Node) Visit(node ast.Node) ast.Visitor {
 | |
| 	switch typedNode := node.(type) {
 | |
| 	case *ast.ForStmt:
 | |
| 		switch init := typedNode.Init.(type) {
 | |
| 		case *ast.AssignStmt:
 | |
| 			for _, lh := range init.Lhs {
 | |
| 				switch tlh := lh.(type) {
 | |
| 				case *ast.Ident:
 | |
| 					f.unsafeObjects[tlh.Obj] = struct{}{}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	case *ast.RangeStmt:
 | |
| 		// Memory variables declarated in range statement
 | |
| 		switch k := typedNode.Key.(type) {
 | |
| 		case *ast.Ident:
 | |
| 			f.unsafeObjects[k.Obj] = struct{}{}
 | |
| 		}
 | |
| 		switch v := typedNode.Value.(type) {
 | |
| 		case *ast.Ident:
 | |
| 			f.unsafeObjects[v.Obj] = struct{}{}
 | |
| 		}
 | |
| 
 | |
| 	case *ast.UnaryExpr:
 | |
| 		if typedNode.Op == token.AND {
 | |
| 			switch ident := typedNode.X.(type) {
 | |
| 			case *ast.Ident:
 | |
| 				if _, unsafe := f.unsafeObjects[ident.Obj]; unsafe {
 | |
| 					f.errorf(ident, "Using a reference for the variable on range scope %s", formatCode(ident.Name, nil))
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	case *ast.Ident:
 | |
| 		if _, obj := f.dangerObjects[typedNode.Obj]; obj {
 | |
| 			// It is the naked variable in scope of range statement.
 | |
| 			f.errorf(node, "Using the variable on range scope %s in function literal", formatCode(typedNode.Name, nil))
 | |
| 			break
 | |
| 		}
 | |
| 
 | |
| 	case *ast.CallExpr:
 | |
| 		// Ignore func literals that'll be called immediately.
 | |
| 		switch funcLit := typedNode.Fun.(type) {
 | |
| 		case *ast.FuncLit:
 | |
| 			f.skipFuncs[funcLit] = struct{}{}
 | |
| 		}
 | |
| 
 | |
| 	case *ast.FuncLit:
 | |
| 		if _, skip := f.skipFuncs[typedNode]; !skip {
 | |
| 			dangers := map[*ast.Object]struct{}{}
 | |
| 			for d := range f.dangerObjects {
 | |
| 				dangers[d] = struct{}{}
 | |
| 			}
 | |
| 			for u := range f.unsafeObjects {
 | |
| 				dangers[u] = struct{}{}
 | |
| 			}
 | |
| 			return &Node{
 | |
| 				fset:          f.fset,
 | |
| 				dangerObjects: dangers,
 | |
| 				unsafeObjects: f.unsafeObjects,
 | |
| 				skipFuncs:     f.skipFuncs,
 | |
| 				issues:        f.issues,
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return f
 | |
| }
 | |
| 
 | |
| // The variadic arguments may start with link and category types,
 | |
| // and must end with a format string and any arguments.
 | |
| // It returns the new Problem.
 | |
| //nolint:interfacer
 | |
| func (f *Node) errorf(n ast.Node, format string, args ...interface{}) {
 | |
| 	pos := f.fset.Position(n.Pos())
 | |
| 	f.errorfAt(pos, format, args...)
 | |
| }
 | |
| 
 | |
| func (f *Node) errorfAt(pos token.Position, format string, args ...interface{}) {
 | |
| 	*f.issues = append(*f.issues, result.Issue{
 | |
| 		Pos:        pos,
 | |
| 		Text:       fmt.Sprintf(format, args...),
 | |
| 		FromLinter: Scopelint{}.Name(),
 | |
| 	})
 | |
| }
 | 
