src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sa1030.go (4705B)


      1 package sa1030
      2 
      3 import (
      4 	"fmt"
      5 	"go/constant"
      6 
      7 	"honnef.co/go/tools/analysis/callcheck"
      8 	"honnef.co/go/tools/analysis/lint"
      9 	"honnef.co/go/tools/internal/passes/buildir"
     10 	"honnef.co/go/tools/knowledge"
     11 
     12 	"golang.org/x/tools/go/analysis"
     13 )
     14 
     15 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     16 	Analyzer: &analysis.Analyzer{
     17 		Name:     "SA1030",
     18 		Requires: []*analysis.Analyzer{buildir.Analyzer},
     19 		Run:      callcheck.Analyzer(rules),
     20 	},
     21 	Doc: &lint.RawDocumentation{
     22 		Title: `Invalid argument in call to a \'strconv\' function`,
     23 		Text: `This check validates the format, number base and bit size arguments of
     24 the various parsing and formatting functions in \'strconv\'.`,
     25 		Since:    "2021.1",
     26 		Severity: lint.SeverityError,
     27 		MergeIf:  lint.MergeIfAny,
     28 	},
     29 })
     30 
     31 var Analyzer = SCAnalyzer.Analyzer
     32 
     33 var rules = map[string]callcheck.Check{
     34 	"strconv.ParseComplex": func(call *callcheck.Call) {
     35 		validateComplexBitSize(call.Args[knowledge.Arg("strconv.ParseComplex.bitSize")])
     36 	},
     37 	"strconv.ParseFloat": func(call *callcheck.Call) {
     38 		validateFloatBitSize(call.Args[knowledge.Arg("strconv.ParseFloat.bitSize")])
     39 	},
     40 	"strconv.ParseInt": func(call *callcheck.Call) {
     41 		validateContinuousBitSize(call.Args[knowledge.Arg("strconv.ParseInt.bitSize")], 0, 64)
     42 		validateIntBaseAllowZero(call.Args[knowledge.Arg("strconv.ParseInt.base")])
     43 	},
     44 	"strconv.ParseUint": func(call *callcheck.Call) {
     45 		validateContinuousBitSize(call.Args[knowledge.Arg("strconv.ParseUint.bitSize")], 0, 64)
     46 		validateIntBaseAllowZero(call.Args[knowledge.Arg("strconv.ParseUint.base")])
     47 	},
     48 
     49 	"strconv.FormatComplex": func(call *callcheck.Call) {
     50 		validateComplexFormat(call.Args[knowledge.Arg("strconv.FormatComplex.fmt")])
     51 		validateComplexBitSize(call.Args[knowledge.Arg("strconv.FormatComplex.bitSize")])
     52 	},
     53 	"strconv.FormatFloat": func(call *callcheck.Call) {
     54 		validateFloatFormat(call.Args[knowledge.Arg("strconv.FormatFloat.fmt")])
     55 		validateFloatBitSize(call.Args[knowledge.Arg("strconv.FormatFloat.bitSize")])
     56 	},
     57 	"strconv.FormatInt": func(call *callcheck.Call) {
     58 		validateIntBase(call.Args[knowledge.Arg("strconv.FormatInt.base")])
     59 	},
     60 	"strconv.FormatUint": func(call *callcheck.Call) {
     61 		validateIntBase(call.Args[knowledge.Arg("strconv.FormatUint.base")])
     62 	},
     63 
     64 	"strconv.AppendFloat": func(call *callcheck.Call) {
     65 		validateFloatFormat(call.Args[knowledge.Arg("strconv.AppendFloat.fmt")])
     66 		validateFloatBitSize(call.Args[knowledge.Arg("strconv.AppendFloat.bitSize")])
     67 	},
     68 	"strconv.AppendInt": func(call *callcheck.Call) {
     69 		validateIntBase(call.Args[knowledge.Arg("strconv.AppendInt.base")])
     70 	},
     71 	"strconv.AppendUint": func(call *callcheck.Call) {
     72 		validateIntBase(call.Args[knowledge.Arg("strconv.AppendUint.base")])
     73 	},
     74 }
     75 
     76 func validateDiscreetBitSize(arg *callcheck.Argument, size1 int, size2 int) {
     77 	if c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil {
     78 		val, _ := constant.Int64Val(c.Value)
     79 		if val != int64(size1) && val != int64(size2) {
     80 			arg.Invalid(fmt.Sprintf("'bitSize' argument is invalid, must be either %d or %d", size1, size2))
     81 		}
     82 	}
     83 }
     84 
     85 func validateComplexBitSize(arg *callcheck.Argument) { validateDiscreetBitSize(arg, 64, 128) }
     86 func validateFloatBitSize(arg *callcheck.Argument)   { validateDiscreetBitSize(arg, 32, 64) }
     87 
     88 func validateContinuousBitSize(arg *callcheck.Argument, min int, max int) {
     89 	if c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil {
     90 		val, _ := constant.Int64Val(c.Value)
     91 		if val < int64(min) || val > int64(max) {
     92 			arg.Invalid(fmt.Sprintf("'bitSize' argument is invalid, must be within %d and %d", min, max))
     93 		}
     94 	}
     95 }
     96 
     97 func validateIntBase(arg *callcheck.Argument) {
     98 	if c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil {
     99 		val, _ := constant.Int64Val(c.Value)
    100 		if val < 2 {
    101 			arg.Invalid("'base' must not be smaller than 2")
    102 		}
    103 		if val > 36 {
    104 			arg.Invalid("'base' must not be larger than 36")
    105 		}
    106 	}
    107 }
    108 
    109 func validateIntBaseAllowZero(arg *callcheck.Argument) {
    110 	if c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil {
    111 		val, _ := constant.Int64Val(c.Value)
    112 		if val < 2 && val != 0 {
    113 			arg.Invalid("'base' must not be smaller than 2, unless it is 0")
    114 		}
    115 		if val > 36 {
    116 			arg.Invalid("'base' must not be larger than 36")
    117 		}
    118 	}
    119 }
    120 
    121 func validateComplexFormat(arg *callcheck.Argument) {
    122 	validateFloatFormat(arg)
    123 }
    124 
    125 func validateFloatFormat(arg *callcheck.Argument) {
    126 	if c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil {
    127 		val, _ := constant.Int64Val(c.Value)
    128 		switch val {
    129 		case 'b', 'e', 'E', 'f', 'g', 'G', 'x', 'X':
    130 		default:
    131 			arg.Invalid(fmt.Sprintf("'fmt' argument is invalid: unknown format %q", rune(val)))
    132 		}
    133 	}
    134 }