github.com/serversong/goreporter@v0.0.0-20200325104552-3cfaf44fd178/linters/simplecode/simple/lint.go (about)

     1  // Package simple contains a linter for Go source code.
     2  package simple // import "github.com/360EntSecGroup-Skylar/goreporter/linters/simplecode/simple"
     3  
     4  import (
     5  	"go/ast"
     6  	"go/constant"
     7  	"go/token"
     8  	"go/types"
     9  	"math"
    10  	"reflect"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/360EntSecGroup-Skylar/goreporter/linters/simplecode/lint"
    15  )
    16  
    17  var Funcs = []lint.Func{
    18  	// LintSingleCaseSelect,
    19  	LintLoopCopy,
    20  	LintIfBoolCmp,
    21  	LintStringsContains,
    22  	LintBytesCompare,
    23  	LintRanges,
    24  	LintForTrue,
    25  	LintRegexpRaw,
    26  	LintIfReturn,
    27  	LintRedundantNilCheckWithLen,
    28  	LintSlicing,
    29  	LintLoopAppend,
    30  	LintTimeSince,
    31  	LintSimplerReturn,
    32  	LintReceiveIntoBlank,
    33  	LintFormatInt,
    34  	LintSimplerStructConversion,
    35  	LintTrim,
    36  }
    37  
    38  func LintSingleCaseSelect(f *lint.File) {
    39  	isSingleSelect := func(node ast.Node) bool {
    40  		v, ok := node.(*ast.SelectStmt)
    41  		if !ok {
    42  			return false
    43  		}
    44  		return len(v.Body.List) == 1
    45  	}
    46  
    47  	seen := map[ast.Node]struct{}{}
    48  	f.Walk(func(node ast.Node) bool {
    49  		switch v := node.(type) {
    50  		case *ast.ForStmt:
    51  			if len(v.Body.List) != 1 {
    52  				return true
    53  			}
    54  			if !isSingleSelect(v.Body.List[0]) {
    55  				return true
    56  			}
    57  			if _, ok := v.Body.List[0].(*ast.SelectStmt).Body.List[0].(*ast.CommClause).Comm.(*ast.SendStmt); ok {
    58  				// Don't suggest using range for channel sends
    59  				return true
    60  			}
    61  			seen[v.Body.List[0]] = struct{}{}
    62  			f.Errorf(node, 1, lint.Category("range-loop"), "should use for range instead of for { select {} }")
    63  		case *ast.SelectStmt:
    64  			if _, ok := seen[v]; ok {
    65  				return true
    66  			}
    67  			if !isSingleSelect(v) {
    68  				return true
    69  			}
    70  			f.Errorf(node, 1, lint.Category("FIXME"), "should use a simple channel send/receive instead of select with a single case")
    71  			return true
    72  		}
    73  		return true
    74  	})
    75  }
    76  
    77  func LintLoopCopy(f *lint.File) {
    78  	fn := func(node ast.Node) bool {
    79  		loop, ok := node.(*ast.RangeStmt)
    80  		if !ok {
    81  			return true
    82  		}
    83  
    84  		if loop.Key == nil {
    85  			return true
    86  		}
    87  		if len(loop.Body.List) != 1 {
    88  			return true
    89  		}
    90  		stmt, ok := loop.Body.List[0].(*ast.AssignStmt)
    91  		if !ok {
    92  			return true
    93  		}
    94  		if stmt.Tok != token.ASSIGN || len(stmt.Lhs) != 1 || len(stmt.Rhs) != 1 {
    95  			return true
    96  		}
    97  		lhs, ok := stmt.Lhs[0].(*ast.IndexExpr)
    98  		if !ok {
    99  			return true
   100  		}
   101  		if _, ok := f.Pkg.TypesInfo.TypeOf(lhs.X).(*types.Slice); !ok {
   102  			return true
   103  		}
   104  		lidx, ok := lhs.Index.(*ast.Ident)
   105  		if !ok {
   106  			return true
   107  		}
   108  		key, ok := loop.Key.(*ast.Ident)
   109  		if !ok {
   110  			return true
   111  		}
   112  		if f.Pkg.TypesInfo.TypeOf(lhs) == nil || f.Pkg.TypesInfo.TypeOf(stmt.Rhs[0]) == nil {
   113  			return true
   114  		}
   115  		if f.Pkg.TypesInfo.ObjectOf(lidx) != f.Pkg.TypesInfo.ObjectOf(key) {
   116  			return true
   117  		}
   118  		if !types.Identical(f.Pkg.TypesInfo.TypeOf(lhs), f.Pkg.TypesInfo.TypeOf(stmt.Rhs[0])) {
   119  			return true
   120  		}
   121  		if _, ok := f.Pkg.TypesInfo.TypeOf(loop.X).(*types.Slice); !ok {
   122  			return true
   123  		}
   124  
   125  		if rhs, ok := stmt.Rhs[0].(*ast.IndexExpr); ok {
   126  			rx, ok := rhs.X.(*ast.Ident)
   127  			_ = rx
   128  			if !ok {
   129  				return true
   130  			}
   131  			ridx, ok := rhs.Index.(*ast.Ident)
   132  			if !ok {
   133  				return true
   134  			}
   135  			if f.Pkg.TypesInfo.ObjectOf(ridx) != f.Pkg.TypesInfo.ObjectOf(key) {
   136  				return true
   137  			}
   138  		} else if rhs, ok := stmt.Rhs[0].(*ast.Ident); ok {
   139  			value, ok := loop.Value.(*ast.Ident)
   140  			if !ok {
   141  				return true
   142  			}
   143  			if f.Pkg.TypesInfo.ObjectOf(rhs) != f.Pkg.TypesInfo.ObjectOf(value) {
   144  				return true
   145  			}
   146  		} else {
   147  			return true
   148  		}
   149  		f.Errorf(loop, 1, lint.Category("FIXME"), "should use copy() instead of a loop")
   150  		return true
   151  	}
   152  	f.Walk(fn)
   153  }
   154  
   155  func LintIfBoolCmp(f *lint.File) {
   156  	fn := func(node ast.Node) bool {
   157  		expr, ok := node.(*ast.BinaryExpr)
   158  		if !ok || (expr.Op != token.EQL && expr.Op != token.NEQ) {
   159  			return true
   160  		}
   161  		x := f.IsBoolConst(expr.X)
   162  		y := f.IsBoolConst(expr.Y)
   163  		if x || y {
   164  			var other ast.Expr
   165  			var val bool
   166  			if x {
   167  				val = f.BoolConst(expr.X)
   168  				other = expr.Y
   169  			} else {
   170  				val = f.BoolConst(expr.Y)
   171  				other = expr.X
   172  			}
   173  			op := ""
   174  			if (expr.Op == token.EQL && !val) || (expr.Op == token.NEQ && val) {
   175  				op = "!"
   176  			}
   177  			f.Errorf(expr, 1, lint.Category("FIXME"), "should omit comparison to bool constant, can be simplified to %s%s",
   178  				op, f.Render(other))
   179  		}
   180  		return true
   181  	}
   182  	f.Walk(fn)
   183  }
   184  
   185  func LintStringsContains(f *lint.File) {
   186  	// map of value to token to bool value
   187  	allowed := map[string]map[token.Token]bool{
   188  		"-1": {token.GTR: true, token.NEQ: true, token.EQL: false},
   189  		"0":  {token.GEQ: true, token.LSS: false},
   190  	}
   191  	fn := func(node ast.Node) bool {
   192  		expr, ok := node.(*ast.BinaryExpr)
   193  		if !ok {
   194  			return true
   195  		}
   196  		switch expr.Op {
   197  		case token.GEQ, token.GTR, token.NEQ, token.LSS, token.EQL:
   198  		default:
   199  			return true
   200  		}
   201  
   202  		value, ok := lint.ExprToInt(expr.Y)
   203  		if !ok {
   204  			return true
   205  		}
   206  
   207  		allowedOps, ok := allowed[value]
   208  		if !ok {
   209  			return true
   210  		}
   211  		b, ok := allowedOps[expr.Op]
   212  		if !ok {
   213  			return true
   214  		}
   215  
   216  		call, ok := expr.X.(*ast.CallExpr)
   217  		if !ok {
   218  			return true
   219  		}
   220  		sel, ok := call.Fun.(*ast.SelectorExpr)
   221  		if !ok {
   222  			return true
   223  		}
   224  		pkgIdent, ok := sel.X.(*ast.Ident)
   225  		if !ok {
   226  			return true
   227  		}
   228  		funIdent := sel.Sel
   229  		if pkgIdent.Name != "strings" && pkgIdent.Name != "bytes" {
   230  			return true
   231  		}
   232  		newFunc := ""
   233  		switch funIdent.Name {
   234  		case "IndexRune":
   235  			newFunc = "ContainsRune"
   236  		case "IndexAny":
   237  			newFunc = "ContainsAny"
   238  		case "Index":
   239  			newFunc = "Contains"
   240  		default:
   241  			return true
   242  		}
   243  
   244  		prefix := ""
   245  		if !b {
   246  			prefix = "!"
   247  		}
   248  		f.Errorf(node, 1, "should use %s%s.%s(%s) instead", prefix, pkgIdent.Name, newFunc, f.RenderArgs(call.Args))
   249  
   250  		return true
   251  	}
   252  	f.Walk(fn)
   253  }
   254  
   255  func LintBytesCompare(f *lint.File) {
   256  	fn := func(node ast.Node) bool {
   257  		expr, ok := node.(*ast.BinaryExpr)
   258  		if !ok {
   259  			return true
   260  		}
   261  		if expr.Op != token.NEQ && expr.Op != token.EQL {
   262  			return true
   263  		}
   264  		call, ok := expr.X.(*ast.CallExpr)
   265  		if !ok {
   266  			return true
   267  		}
   268  		if !lint.IsPkgDot(call.Fun, "bytes", "Compare") {
   269  			return true
   270  		}
   271  		value, ok := lint.ExprToInt(expr.Y)
   272  		if !ok {
   273  			return true
   274  		}
   275  		if value != "0" {
   276  			return true
   277  		}
   278  		args := f.RenderArgs(call.Args)
   279  		prefix := ""
   280  		if expr.Op == token.NEQ {
   281  			prefix = "!"
   282  		}
   283  		f.Errorf(node, 1, lint.Category("FIXME"), "should use %sbytes.Equal(%s) instead", prefix, args)
   284  		return true
   285  	}
   286  	f.Walk(fn)
   287  }
   288  
   289  func LintRanges(f *lint.File) {
   290  	f.Walk(func(node ast.Node) bool {
   291  		rs, ok := node.(*ast.RangeStmt)
   292  		if !ok {
   293  			return true
   294  		}
   295  		if lint.IsIdent(rs.Key, "_") && (rs.Value == nil || lint.IsIdent(rs.Value, "_")) {
   296  			f.Errorf(rs.Key, 1, lint.Category("range-loop"), "should omit values from range; this loop is equivalent to `for range ...`")
   297  		}
   298  
   299  		return true
   300  	})
   301  }
   302  
   303  func LintForTrue(f *lint.File) {
   304  	fn := func(node ast.Node) bool {
   305  		loop, ok := node.(*ast.ForStmt)
   306  		if !ok {
   307  			return true
   308  		}
   309  		if loop.Init != nil || loop.Post != nil {
   310  			return true
   311  		}
   312  		if !f.IsBoolConst(loop.Cond) || !f.BoolConst(loop.Cond) {
   313  			return true
   314  		}
   315  		f.Errorf(loop, 1, lint.Category("FIXME"), "should use for {} instead of for true {}")
   316  		return true
   317  	}
   318  	f.Walk(fn)
   319  }
   320  
   321  func LintRegexpRaw(f *lint.File) {
   322  	fn := func(node ast.Node) bool {
   323  		call, ok := node.(*ast.CallExpr)
   324  		if !ok {
   325  			return true
   326  		}
   327  		sel, ok := call.Fun.(*ast.SelectorExpr)
   328  		if !ok {
   329  			return true
   330  		}
   331  		if !lint.IsPkgDot(call.Fun, "regexp", "MustCompile") && !lint.IsPkgDot(call.Fun, "regexp", "Compile") {
   332  			return true
   333  		}
   334  		if len(call.Args) != 1 {
   335  			// invalid function call
   336  			return true
   337  		}
   338  		lit, ok := call.Args[0].(*ast.BasicLit)
   339  		if !ok {
   340  			// TODO(dominikh): support string concat, maybe support constants
   341  			return true
   342  		}
   343  		if lit.Kind != token.STRING {
   344  			// invalid function call
   345  			return true
   346  		}
   347  		if f.Src[f.Fset.Position(lit.Pos()).Offset] != '"' {
   348  			// already a raw string
   349  			return true
   350  		}
   351  		val := lit.Value
   352  		if !strings.Contains(val, `\\`) {
   353  			return true
   354  		}
   355  
   356  		bs := false
   357  		for _, c := range val {
   358  			if !bs && c == '\\' {
   359  				bs = true
   360  				continue
   361  			}
   362  			if bs && c == '\\' {
   363  				bs = false
   364  				continue
   365  			}
   366  			if bs {
   367  				// backslash followed by non-backslash -> escape sequence
   368  				return true
   369  			}
   370  		}
   371  
   372  		f.Errorf(call, 1, lint.Category("FIXME"), "should use raw string (`...`) with regexp.%s to avoid having to escape twice", sel.Sel.Name)
   373  		return true
   374  	}
   375  	f.Walk(fn)
   376  }
   377  
   378  func LintIfReturn(f *lint.File) {
   379  	fn := func(node ast.Node) bool {
   380  		block, ok := node.(*ast.BlockStmt)
   381  		if !ok {
   382  			return true
   383  		}
   384  		l := len(block.List)
   385  		if l < 2 {
   386  			return true
   387  		}
   388  		n1, n2 := block.List[l-2], block.List[l-1]
   389  
   390  		if len(block.List) >= 3 {
   391  			if _, ok := block.List[l-3].(*ast.IfStmt); ok {
   392  				// Do not flag a series of if statements
   393  				return true
   394  			}
   395  		}
   396  		// if statement with no init, no else, a single condition
   397  		// checking an identifier or function call and just a return
   398  		// statement in the body, that returns a boolean constant
   399  		ifs, ok := n1.(*ast.IfStmt)
   400  		if !ok {
   401  			return true
   402  		}
   403  		if ifs.Else != nil || ifs.Init != nil {
   404  			return true
   405  		}
   406  		if len(ifs.Body.List) != 1 {
   407  			return true
   408  		}
   409  		if op, ok := ifs.Cond.(*ast.BinaryExpr); ok {
   410  			switch op.Op {
   411  			case token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ:
   412  			default:
   413  				return true
   414  			}
   415  		}
   416  		ret1, ok := ifs.Body.List[0].(*ast.ReturnStmt)
   417  		if !ok {
   418  			return true
   419  		}
   420  		if len(ret1.Results) != 1 {
   421  			return true
   422  		}
   423  		if !f.IsBoolConst(ret1.Results[0]) {
   424  			return true
   425  		}
   426  
   427  		ret2, ok := n2.(*ast.ReturnStmt)
   428  		if !ok {
   429  			return true
   430  		}
   431  		if len(ret2.Results) != 1 {
   432  			return true
   433  		}
   434  		if !f.IsBoolConst(ret2.Results[0]) {
   435  			return true
   436  		}
   437  		f.Errorf(n1, 1, lint.Category("FIXME"), "should use 'return <expr>' instead of 'if <expr> { return <bool> }; return <bool>'")
   438  		return true
   439  	}
   440  	f.Walk(fn)
   441  }
   442  
   443  // LintRedundantNilCheckWithLen checks for the following reduntant nil-checks:
   444  //
   445  //   if x == nil || len(x) == 0 {}
   446  //   if x != nil && len(x) != 0 {}
   447  //   if x != nil && len(x) == N {} (where N != 0)
   448  //   if x != nil && len(x) > N {}
   449  //   if x != nil && len(x) >= N {} (where N != 0)
   450  //
   451  func LintRedundantNilCheckWithLen(f *lint.File) {
   452  	isConstZero := func(expr ast.Expr) (isConst bool, isZero bool) {
   453  		lit, ok := expr.(*ast.BasicLit)
   454  		if ok {
   455  			return true, lit.Kind == token.INT && lit.Value == "0"
   456  		}
   457  		id, ok := expr.(*ast.Ident)
   458  		if !ok {
   459  			return false, false
   460  		}
   461  		c, ok := f.Pkg.TypesInfo.ObjectOf(id).(*types.Const)
   462  		if !ok {
   463  			return false, false
   464  		}
   465  		return true, c.Val().Kind() == constant.Int && c.Val().String() == "0"
   466  	}
   467  
   468  	fn := func(node ast.Node) bool {
   469  		// check that expr is "x || y" or "x && y"
   470  		expr, ok := node.(*ast.BinaryExpr)
   471  		if !ok {
   472  			return true
   473  		}
   474  		if expr.Op != token.LOR && expr.Op != token.LAND {
   475  			return true
   476  		}
   477  		eqNil := expr.Op == token.LOR
   478  
   479  		// check that x is "xx == nil" or "xx != nil"
   480  		x, ok := expr.X.(*ast.BinaryExpr)
   481  		if !ok {
   482  			return true
   483  		}
   484  		if eqNil && x.Op != token.EQL {
   485  			return true
   486  		}
   487  		if !eqNil && x.Op != token.NEQ {
   488  			return true
   489  		}
   490  		xx, ok := x.X.(*ast.Ident)
   491  		if !ok {
   492  			return true
   493  		}
   494  		if !lint.IsNil(x.Y) {
   495  			return true
   496  		}
   497  
   498  		// check that y is "len(xx) == 0" or "len(xx) ... "
   499  		y, ok := expr.Y.(*ast.BinaryExpr)
   500  		if !ok {
   501  			return true
   502  		}
   503  		if eqNil && y.Op != token.EQL { // must be len(xx) *==* 0
   504  			return false
   505  		}
   506  		yx, ok := y.X.(*ast.CallExpr)
   507  		if !ok {
   508  			return true
   509  		}
   510  		yxFun, ok := yx.Fun.(*ast.Ident)
   511  		if !ok || yxFun.Name != "len" || len(yx.Args) != 1 {
   512  			return true
   513  		}
   514  		yxArg, ok := yx.Args[0].(*ast.Ident)
   515  		if !ok {
   516  			return true
   517  		}
   518  		if yxArg.Name != xx.Name {
   519  			return true
   520  		}
   521  
   522  		if eqNil && !lint.IsZero(y.Y) { // must be len(x) == *0*
   523  			return true
   524  		}
   525  
   526  		if !eqNil {
   527  			isConst, isZero := isConstZero(y.Y)
   528  			if !isConst {
   529  				return true
   530  			}
   531  			switch y.Op {
   532  			case token.EQL:
   533  				// avoid false positive for "xx != nil && len(xx) == 0"
   534  				if isZero {
   535  					return true
   536  				}
   537  			case token.GEQ:
   538  				// avoid false positive for "xx != nil && len(xx) >= 0"
   539  				if isZero {
   540  					return true
   541  				}
   542  			case token.NEQ:
   543  				// avoid false positive for "xx != nil && len(xx) != <non-zero>"
   544  				if !isZero {
   545  					return true
   546  				}
   547  			case token.GTR:
   548  				// ok
   549  			default:
   550  				return true
   551  			}
   552  		}
   553  
   554  		// finally check that xx type is one of array, slice, map or chan
   555  		// this is to prevent false positive in case if xx is a pointer to an array
   556  		var nilType string
   557  		switch f.Pkg.TypesInfo.TypeOf(xx).(type) {
   558  		case *types.Slice:
   559  			nilType = "nil slices"
   560  		case *types.Map:
   561  			nilType = "nil maps"
   562  		case *types.Chan:
   563  			nilType = "nil channels"
   564  		default:
   565  			return true
   566  		}
   567  		f.Errorf(expr, 1, lint.Category("FIXME"), "should omit nil check; len() for %s is defined as zero", nilType)
   568  		return true
   569  	}
   570  	f.Walk(fn)
   571  }
   572  
   573  func LintSlicing(f *lint.File) {
   574  	fn := func(node ast.Node) bool {
   575  		n, ok := node.(*ast.SliceExpr)
   576  		if !ok {
   577  			return true
   578  		}
   579  		if n.Max != nil {
   580  			return true
   581  		}
   582  		s, ok := n.X.(*ast.Ident)
   583  		if !ok || s.Obj == nil {
   584  			return true
   585  		}
   586  		call, ok := n.High.(*ast.CallExpr)
   587  		if !ok || len(call.Args) != 1 || call.Ellipsis.IsValid() {
   588  			return true
   589  		}
   590  		fun, ok := call.Fun.(*ast.Ident)
   591  		if !ok || fun.Name != "len" {
   592  			return true
   593  		}
   594  		if _, ok := f.Pkg.TypesInfo.ObjectOf(fun).(*types.Builtin); !ok {
   595  			return true
   596  		}
   597  		arg, ok := call.Args[0].(*ast.Ident)
   598  		if !ok || arg.Obj != s.Obj {
   599  			return true
   600  		}
   601  		f.Errorf(n, 1, "should omit second index in slice, s[a:len(s)] is identical to s[a:]")
   602  		return true
   603  	}
   604  	f.Walk(fn)
   605  }
   606  
   607  func refersTo(info *types.Info, expr ast.Expr, ident *ast.Ident) bool {
   608  	found := false
   609  	fn := func(node ast.Node) bool {
   610  		ident2, ok := node.(*ast.Ident)
   611  		if !ok {
   612  			return true
   613  		}
   614  		if info.ObjectOf(ident) == info.ObjectOf(ident2) {
   615  			found = true
   616  			return false
   617  		}
   618  		return true
   619  	}
   620  	ast.Inspect(expr, fn)
   621  	return found
   622  }
   623  
   624  func LintLoopAppend(f *lint.File) {
   625  	fn := func(node ast.Node) bool {
   626  		loop, ok := node.(*ast.RangeStmt)
   627  		if !ok {
   628  			return true
   629  		}
   630  		if !lint.IsBlank(loop.Key) {
   631  			return true
   632  		}
   633  		val, ok := loop.Value.(*ast.Ident)
   634  		if !ok {
   635  			return true
   636  		}
   637  		if len(loop.Body.List) != 1 {
   638  			return true
   639  		}
   640  		stmt, ok := loop.Body.List[0].(*ast.AssignStmt)
   641  		if !ok {
   642  			return true
   643  		}
   644  		if stmt.Tok != token.ASSIGN || len(stmt.Lhs) != 1 || len(stmt.Rhs) != 1 {
   645  			return true
   646  		}
   647  		if refersTo(f.Pkg.TypesInfo, stmt.Lhs[0], val) {
   648  			return true
   649  		}
   650  		call, ok := stmt.Rhs[0].(*ast.CallExpr)
   651  		if !ok {
   652  			return true
   653  		}
   654  		if len(call.Args) != 2 || call.Ellipsis.IsValid() {
   655  			return true
   656  		}
   657  		fun, ok := call.Fun.(*ast.Ident)
   658  		if !ok {
   659  			return true
   660  		}
   661  		obj := f.Pkg.TypesInfo.ObjectOf(fun)
   662  		fn, ok := obj.(*types.Builtin)
   663  		if !ok || fn.Name() != "append" {
   664  			return true
   665  		}
   666  
   667  		src := f.Pkg.TypesInfo.TypeOf(loop.X)
   668  		dst := f.Pkg.TypesInfo.TypeOf(call.Args[0])
   669  		// TODO(dominikh) remove nil check once Go issue #15173 has
   670  		// been fixed
   671  		if src == nil {
   672  			return true
   673  		}
   674  		if !types.Identical(src, dst) {
   675  			return true
   676  		}
   677  
   678  		if f.Render(stmt.Lhs[0]) != f.Render(call.Args[0]) {
   679  			return true
   680  		}
   681  
   682  		el, ok := call.Args[1].(*ast.Ident)
   683  		if !ok {
   684  			return true
   685  		}
   686  		if f.Pkg.TypesInfo.ObjectOf(val) != f.Pkg.TypesInfo.ObjectOf(el) {
   687  			return true
   688  		}
   689  		f.Errorf(loop, 1, "should replace loop with %s = append(%s, %s...)",
   690  			f.Render(stmt.Lhs[0]), f.Render(call.Args[0]), f.Render(loop.X))
   691  		return true
   692  	}
   693  	f.Walk(fn)
   694  }
   695  
   696  func LintTimeSince(f *lint.File) {
   697  	fn := func(node ast.Node) bool {
   698  		call, ok := node.(*ast.CallExpr)
   699  		if !ok {
   700  			return true
   701  		}
   702  		sel, ok := call.Fun.(*ast.SelectorExpr)
   703  		if !ok {
   704  			return true
   705  		}
   706  		subcall, ok := sel.X.(*ast.CallExpr)
   707  		if !ok {
   708  			return true
   709  		}
   710  		if !lint.IsPkgDot(subcall.Fun, "time", "Now") {
   711  			return true
   712  		}
   713  		if sel.Sel.Name != "Sub" {
   714  			return true
   715  		}
   716  		f.Errorf(call, 1, "should use time.Since instead of time.Now().Sub")
   717  		return true
   718  	}
   719  	f.Walk(fn)
   720  }
   721  
   722  func LintSimplerReturn(f *lint.File) {
   723  	fn1 := func(node ast.Node) bool {
   724  		var ret *ast.FieldList
   725  		switch x := node.(type) {
   726  		case *ast.FuncDecl:
   727  			ret = x.Type.Results
   728  		case *ast.FuncLit:
   729  			ret = x.Type.Results
   730  		default:
   731  			return true
   732  		}
   733  		if ret == nil {
   734  			return true
   735  		}
   736  
   737  		fn2 := func(node ast.Node) bool {
   738  			block, ok := node.(*ast.BlockStmt)
   739  			if !ok {
   740  				return true
   741  			}
   742  			if len(block.List) < 2 {
   743  				return true
   744  			}
   745  
   746  		outer:
   747  			for i, stmt := range block.List {
   748  				if i == len(block.List)-1 {
   749  					break
   750  				}
   751  				if i > 0 {
   752  					// don't flag an if in a series of ifs
   753  					if _, ok := block.List[i-1].(*ast.IfStmt); ok {
   754  						continue
   755  					}
   756  				}
   757  
   758  				// if <id1> != nil
   759  				ifs, ok := stmt.(*ast.IfStmt)
   760  				if !ok || len(ifs.Body.List) != 1 || ifs.Else != nil {
   761  					continue
   762  				}
   763  				expr, ok := ifs.Cond.(*ast.BinaryExpr)
   764  				if !ok || expr.Op != token.NEQ || !lint.IsNil(expr.Y) {
   765  					continue
   766  				}
   767  				id1, ok := expr.X.(*ast.Ident)
   768  				if !ok {
   769  					continue
   770  				}
   771  
   772  				// return ..., <id1>
   773  				ret1, ok := ifs.Body.List[0].(*ast.ReturnStmt)
   774  				if !ok || len(ret1.Results) == 0 {
   775  					continue
   776  				}
   777  				var results1 []types.Object
   778  				for _, res := range ret1.Results {
   779  					ident, ok := res.(*ast.Ident)
   780  					if !ok {
   781  						continue outer
   782  					}
   783  					results1 = append(results1, f.Pkg.TypesInfo.ObjectOf(ident))
   784  				}
   785  				if results1[len(results1)-1] != f.Pkg.TypesInfo.ObjectOf(id1) {
   786  					continue
   787  				}
   788  
   789  				// return ..., [<id1> | nil]
   790  				ret2, ok := block.List[i+1].(*ast.ReturnStmt)
   791  				if !ok || len(ret2.Results) == 0 {
   792  					continue
   793  				}
   794  				var results2 []types.Object
   795  				for _, res := range ret2.Results {
   796  					ident, ok := res.(*ast.Ident)
   797  					if !ok {
   798  						continue outer
   799  					}
   800  					results2 = append(results2, f.Pkg.TypesInfo.ObjectOf(ident))
   801  				}
   802  				_, isNil := results2[len(results2)-1].(*types.Nil)
   803  				if results2[len(results2)-1] != f.Pkg.TypesInfo.ObjectOf(id1) &&
   804  					!isNil {
   805  					continue
   806  				}
   807  				for i, v := range results1[:len(results1)-1] {
   808  					if v != results2[i] {
   809  						continue outer
   810  					}
   811  				}
   812  
   813  				id1Obj := f.Pkg.TypesInfo.ObjectOf(id1)
   814  				if id1Obj == nil {
   815  					continue
   816  				}
   817  				_, idIface := id1Obj.Type().Underlying().(*types.Interface)
   818  				_, retIface := f.Pkg.TypesInfo.TypeOf(ret.List[len(ret.List)-1].Type).Underlying().(*types.Interface)
   819  
   820  				if retIface && !idIface {
   821  					// When the return value is an interface, but the
   822  					// identifier is not, an explicit check for nil is
   823  					// required to return an untyped nil.
   824  					continue
   825  				}
   826  
   827  				f.Errorf(ifs, 1, "'if %s != nil { return %s }; return %s' can be simplified to 'return %s'",
   828  					f.Render(expr.X), f.RenderArgs(ret1.Results),
   829  					f.RenderArgs(ret2.Results), f.RenderArgs(ret1.Results))
   830  			}
   831  			return true
   832  		}
   833  		ast.Inspect(node, fn2)
   834  		return true
   835  	}
   836  	f.Walk(fn1)
   837  }
   838  
   839  func LintReceiveIntoBlank(f *lint.File) {
   840  	fn := func(node ast.Node) bool {
   841  		stmt, ok := node.(*ast.AssignStmt)
   842  		if !ok {
   843  			return true
   844  		}
   845  		if len(stmt.Lhs) != len(stmt.Rhs) {
   846  			return true
   847  		}
   848  		for i, lh := range stmt.Lhs {
   849  			rh := stmt.Rhs[i]
   850  			if !lint.IsBlank(lh) {
   851  				continue
   852  			}
   853  			expr, ok := rh.(*ast.UnaryExpr)
   854  			if !ok {
   855  				continue
   856  			}
   857  			if expr.Op != token.ARROW {
   858  				continue
   859  			}
   860  			f.Errorf(lh, 1, "'_ = <-ch' can be simplified to '<-ch'")
   861  		}
   862  		return true
   863  	}
   864  	f.Walk(fn)
   865  }
   866  
   867  func LintFormatInt(f *lint.File) {
   868  	checkBasic := func(v ast.Expr) bool {
   869  		typ, ok := f.Pkg.TypesInfo.TypeOf(v).(*types.Basic)
   870  		if !ok {
   871  			return false
   872  		}
   873  		switch typ.Kind() {
   874  		case types.Int, types.Int32:
   875  			return true
   876  		}
   877  		return false
   878  	}
   879  	checkConst := func(v *ast.Ident) bool {
   880  		c, ok := f.Pkg.TypesInfo.ObjectOf(v).(*types.Const)
   881  		if !ok {
   882  			return false
   883  		}
   884  		if c.Val().Kind() != constant.Int {
   885  			return false
   886  		}
   887  		i, _ := constant.Int64Val(c.Val())
   888  		return i <= math.MaxInt32
   889  	}
   890  	checkConstStrict := func(v *ast.Ident) bool {
   891  		if !checkConst(v) {
   892  			return false
   893  		}
   894  		basic, ok := f.Pkg.TypesInfo.ObjectOf(v).(*types.Const).Type().(*types.Basic)
   895  		return ok && basic.Kind() == types.UntypedInt
   896  	}
   897  
   898  	fn := func(node ast.Node) bool {
   899  		call, ok := node.(*ast.CallExpr)
   900  		if !ok {
   901  			return true
   902  		}
   903  		if !lint.IsPkgDot(call.Fun, "strconv", "FormatInt") {
   904  			return true
   905  		}
   906  		if len(call.Args) != 2 {
   907  			return true
   908  		}
   909  		if lit, ok := call.Args[1].(*ast.BasicLit); !ok || lit.Value != "10" {
   910  			return true
   911  		}
   912  
   913  		matches := false
   914  		switch v := call.Args[0].(type) {
   915  		case *ast.CallExpr:
   916  			if len(v.Args) != 1 {
   917  				return true
   918  			}
   919  			ident, ok := v.Fun.(*ast.Ident)
   920  			if !ok {
   921  				return true
   922  			}
   923  			obj, ok := f.Pkg.TypesInfo.ObjectOf(ident).(*types.TypeName)
   924  			if !ok || obj.Parent() != types.Universe || obj.Name() != "int64" {
   925  				return true
   926  			}
   927  
   928  			switch vv := v.Args[0].(type) {
   929  			case *ast.BasicLit:
   930  				i, _ := strconv.ParseInt(vv.Value, 10, 64)
   931  				if i <= math.MaxInt32 {
   932  					matches = true
   933  				}
   934  			case *ast.Ident:
   935  				if checkConst(vv) || checkBasic(v.Args[0]) {
   936  					matches = true
   937  				}
   938  			default:
   939  				if checkBasic(v.Args[0]) {
   940  					matches = true
   941  				}
   942  			}
   943  		case *ast.BasicLit:
   944  			if v.Kind != token.INT {
   945  				return true
   946  			}
   947  			i, _ := strconv.ParseInt(v.Value, 10, 64)
   948  			if i <= math.MaxInt32 {
   949  				matches = true
   950  			}
   951  		case *ast.Ident:
   952  			if checkConstStrict(v) {
   953  				matches = true
   954  			}
   955  		}
   956  		if matches {
   957  			f.Errorf(call, 1, "should use strconv.Itoa instead of strconv.FormatInt")
   958  		}
   959  		return true
   960  	}
   961  	f.Walk(fn)
   962  }
   963  
   964  func LintSimplerStructConversion(f *lint.File) {
   965  	fn := func(node ast.Node) bool {
   966  		lit, ok := node.(*ast.CompositeLit)
   967  		if !ok {
   968  			return true
   969  		}
   970  		typ1 := f.Pkg.TypesInfo.TypeOf(lit.Type)
   971  		if typ1 == nil {
   972  			return true
   973  		}
   974  		// FIXME support pointer to struct
   975  		s1, ok := typ1.Underlying().(*types.Struct)
   976  		if !ok {
   977  			return true
   978  		}
   979  
   980  		n := s1.NumFields()
   981  		var typ2 types.Type
   982  		var ident *ast.Ident
   983  		getSelType := func(expr ast.Expr) (types.Type, *ast.Ident, bool) {
   984  			sel, ok := expr.(*ast.SelectorExpr)
   985  			if !ok {
   986  				return nil, nil, false
   987  			}
   988  			ident, ok := sel.X.(*ast.Ident)
   989  			if !ok {
   990  				return nil, nil, false
   991  			}
   992  			typ := f.Pkg.TypesInfo.TypeOf(sel.X)
   993  			return typ, ident, typ != nil
   994  		}
   995  		if len(lit.Elts) == 0 {
   996  			return true
   997  		}
   998  		for i, elt := range lit.Elts {
   999  			n--
  1000  			var t types.Type
  1001  			var id *ast.Ident
  1002  			var ok bool
  1003  			switch elt := elt.(type) {
  1004  			case *ast.SelectorExpr:
  1005  				t, id, ok = getSelType(elt)
  1006  				if !ok {
  1007  					return true
  1008  				}
  1009  				if i >= s1.NumFields() || s1.Field(i).Name() != elt.Sel.Name {
  1010  					return true
  1011  				}
  1012  			case *ast.KeyValueExpr:
  1013  				var sel *ast.SelectorExpr
  1014  				sel, ok = elt.Value.(*ast.SelectorExpr)
  1015  				if !ok {
  1016  					return true
  1017  				}
  1018  
  1019  				if elt.Key.(*ast.Ident).Name != sel.Sel.Name {
  1020  					return true
  1021  				}
  1022  				t, id, ok = getSelType(elt.Value)
  1023  			}
  1024  			if !ok {
  1025  				return true
  1026  			}
  1027  			if typ2 != nil && typ2 != t {
  1028  				return true
  1029  			}
  1030  			if ident != nil && ident.Obj != id.Obj {
  1031  				return true
  1032  			}
  1033  			typ2 = t
  1034  			ident = id
  1035  		}
  1036  
  1037  		if n != 0 {
  1038  			return true
  1039  		}
  1040  
  1041  		if typ2 == nil {
  1042  			return true
  1043  		}
  1044  
  1045  		s2, ok := typ2.Underlying().(*types.Struct)
  1046  		if !ok {
  1047  			return true
  1048  		}
  1049  		if typ1 == typ2 {
  1050  			return true
  1051  		}
  1052  		if !structsIdentical(s1, s2) {
  1053  			return true
  1054  		}
  1055  		f.Errorf(node, 1, "should use type conversion instead of struct literal")
  1056  		return true
  1057  	}
  1058  	f.Walk(fn)
  1059  }
  1060  
  1061  func LintTrim(f *lint.File) {
  1062  	// TODO(dh): implement the same for suffix
  1063  	sameNonDynamic := func(node1, node2 ast.Node) bool {
  1064  		if reflect.TypeOf(node1) != reflect.TypeOf(node2) {
  1065  			return false
  1066  		}
  1067  
  1068  		switch node1 := node1.(type) {
  1069  		case *ast.Ident:
  1070  			return node1.Obj == node2.(*ast.Ident).Obj
  1071  		case *ast.SelectorExpr:
  1072  			return f.Render(node1) == f.Render(node2)
  1073  		case *ast.IndexExpr:
  1074  			return f.Render(node1) == f.Render(node2)
  1075  		}
  1076  		return false
  1077  	}
  1078  
  1079  	isLenOnIdent := func(fn ast.Expr, ident ast.Expr) bool {
  1080  		call, ok := fn.(*ast.CallExpr)
  1081  		if !ok {
  1082  			return false
  1083  		}
  1084  		if fn, ok := call.Fun.(*ast.Ident); !ok || fn.Name != "len" {
  1085  			return false
  1086  		}
  1087  		if len(call.Args) != 1 {
  1088  			return false
  1089  		}
  1090  		return sameNonDynamic(call.Args[0], ident)
  1091  	}
  1092  
  1093  	fn := func(node ast.Node) bool {
  1094  		var pkg string
  1095  		var fun string
  1096  
  1097  		ifstmt, ok := node.(*ast.IfStmt)
  1098  		if !ok {
  1099  			return true
  1100  		}
  1101  		if ifstmt.Init != nil {
  1102  			return true
  1103  		}
  1104  		if ifstmt.Else != nil {
  1105  			return true
  1106  		}
  1107  		if len(ifstmt.Body.List) != 1 {
  1108  			return true
  1109  		}
  1110  		condCall, ok := ifstmt.Cond.(*ast.CallExpr)
  1111  		if !ok {
  1112  			return true
  1113  		}
  1114  		call, ok := condCall.Fun.(*ast.SelectorExpr)
  1115  		if !ok {
  1116  			return true
  1117  		}
  1118  		if lint.IsIdent(call.X, "strings") {
  1119  			pkg = "strings"
  1120  		} else if lint.IsIdent(call.X, "bytes") {
  1121  			pkg = "bytes"
  1122  		} else {
  1123  			return true
  1124  		}
  1125  		if lint.IsIdent(call.Sel, "HasPrefix") {
  1126  			fun = "HasPrefix"
  1127  		} else if lint.IsIdent(call.Sel, "HasSuffix") {
  1128  			fun = "HasSuffix"
  1129  		} else {
  1130  			return true
  1131  		}
  1132  
  1133  		assign, ok := ifstmt.Body.List[0].(*ast.AssignStmt)
  1134  		if !ok {
  1135  			return true
  1136  		}
  1137  		if assign.Tok != token.ASSIGN {
  1138  			return true
  1139  		}
  1140  		if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {
  1141  			return true
  1142  		}
  1143  		if !sameNonDynamic(condCall.Args[0], assign.Lhs[0]) {
  1144  			return true
  1145  		}
  1146  		slice, ok := assign.Rhs[0].(*ast.SliceExpr)
  1147  		if !ok {
  1148  			return true
  1149  		}
  1150  		if slice.Slice3 {
  1151  			return true
  1152  		}
  1153  		if !sameNonDynamic(slice.X, condCall.Args[0]) {
  1154  			return true
  1155  		}
  1156  		var index ast.Expr
  1157  		switch fun {
  1158  		case "HasPrefix":
  1159  			// TODO(dh) We could detect a High that is len(s), but another
  1160  			// rule will already flag that, anyway.
  1161  			if slice.High != nil {
  1162  				return true
  1163  			}
  1164  			index = slice.Low
  1165  		case "HasSuffix":
  1166  			if slice.Low != nil {
  1167  				n, ok := intLit(f, slice.Low)
  1168  				if !ok || n != 0 {
  1169  					return true
  1170  				}
  1171  			}
  1172  			index = slice.High
  1173  		}
  1174  
  1175  		switch index := index.(type) {
  1176  		case *ast.CallExpr:
  1177  			if fun != "HasPrefix" {
  1178  				return true
  1179  			}
  1180  			if fn, ok := index.Fun.(*ast.Ident); !ok || fn.Name != "len" {
  1181  				return true
  1182  			}
  1183  			if len(index.Args) != 1 {
  1184  				return true
  1185  			}
  1186  			id3 := index.Args[0]
  1187  			switch oid3 := condCall.Args[1].(type) {
  1188  			case *ast.BasicLit:
  1189  				if pkg != "strings" {
  1190  					return false
  1191  				}
  1192  				lit, ok := id3.(*ast.BasicLit)
  1193  				if !ok {
  1194  					return true
  1195  				}
  1196  				s1, ok1 := stringLit(f, lit)
  1197  				s2, ok2 := stringLit(f, condCall.Args[1])
  1198  				if !ok1 || !ok2 || s1 != s2 {
  1199  					return true
  1200  				}
  1201  			default:
  1202  				if !sameNonDynamic(id3, oid3) {
  1203  					return true
  1204  				}
  1205  			}
  1206  		case *ast.BasicLit, *ast.Ident:
  1207  			if fun != "HasPrefix" {
  1208  				return true
  1209  			}
  1210  			if pkg != "strings" {
  1211  				return true
  1212  			}
  1213  			string, ok1 := stringLit(f, condCall.Args[1])
  1214  			int, ok2 := intLit(f, slice.Low)
  1215  			if !ok1 || !ok2 || int != int64(len(string)) {
  1216  				return true
  1217  			}
  1218  		case *ast.BinaryExpr:
  1219  			if fun != "HasSuffix" {
  1220  				return true
  1221  			}
  1222  			if index.Op != token.SUB {
  1223  				return true
  1224  			}
  1225  			if !isLenOnIdent(index.X, condCall.Args[0]) ||
  1226  				!isLenOnIdent(index.Y, condCall.Args[1]) {
  1227  				return true
  1228  			}
  1229  		default:
  1230  			return true
  1231  		}
  1232  
  1233  		var replacement string
  1234  		switch fun {
  1235  		case "HasPrefix":
  1236  			replacement = "TrimPrefix"
  1237  		case "HasSuffix":
  1238  			replacement = "TrimSuffix"
  1239  		}
  1240  		f.Errorf(ifstmt, 1, "should replace this if statement with an unconditional %s.%s", pkg, replacement)
  1241  		return true
  1242  	}
  1243  	f.Walk(fn)
  1244  }
  1245  
  1246  func stringLit(f *lint.File, expr ast.Expr) (string, bool) {
  1247  	tv := f.Pkg.TypesInfo.Types[expr]
  1248  	if tv.Value == nil {
  1249  		return "", false
  1250  	}
  1251  	if tv.Value.Kind() != constant.String {
  1252  		return "", false
  1253  	}
  1254  	return constant.StringVal(tv.Value), true
  1255  }
  1256  
  1257  func intLit(f *lint.File, expr ast.Expr) (int64, bool) {
  1258  	tv := f.Pkg.TypesInfo.Types[expr]
  1259  	if tv.Value == nil {
  1260  		return 0, false
  1261  	}
  1262  	if tv.Value.Kind() != constant.Int {
  1263  		return 0, false
  1264  	}
  1265  	val, _ := constant.Int64Val(tv.Value)
  1266  	return val, true
  1267  }