Golang lint&Nilaway在IDE集成和配置

320 阅读11分钟

Linters

虽然IDE已经有插件可以执行静态代码分析,但golangci-lint依然有大量自定义Linter值得引入和执行静态代码扫描,提高代码质量

golangci-lint的Linters golangci-lint.run/usage/linte…

三方Linters github.com/golangci/aw…

以下为golangci-lint在VSCode中集成和使用,包括Linters以及Nilaway

.custom-gcl.yml文件

在项目工程根目录,创建/修改.golangci.yml文件

这里推荐一些Linter,以及对应配置,详见如下(涉及安全、风格、性能)

enable 启用的插件清单

linters-settings插件的配置

配置可复制直接使用

vim .golangci.yml

linters:
  # Disable all linters.
  # Default: false
  disable-all: true
  # Enable specific linter
  # https://golangci-lint.run/usage/linters/#enabled-by-default-linters
  enable:
    # default linter
    # deadcode被弃用, 请使用unused
    #- deadcode 
    - errcheck
    - gosimple
    - govet
    - ineffassign
    - staticcheck
    - typecheck
    - unused
    # varcheck被弃用
    #- varcheck
    # 新增 linter
    - gocyclo
    - gofmt
    - goimports

    # 检查 HTTP 响应主体是否成功关闭。
    - bodyclose
    # 检查传递给 json 编码函数的类型。报告不支持的类型并报告可以省略对返回错误的检查的情况。	
    - errchkjson
    # 对 Go“和类型”运行详尽性检查。
    - gochecksumtype
    # 查找即使检查错误不为零也会返回零的代码。	
    - nilerr
    # 检查该函数是否使用非继承的上下文。
    - contextcheck
    # 检查 sql.Rows、sql.Stmt、sqlx.NamedStmt 和 pgx.Query 是否已关闭。	
    - sqlclosecheck
    # 检查外部包返回的错误是否被包装。	
    - wrapcheck

    # Copyloopvar 是一个用于检测循环变量被复制的地方的 linter。	
    - copyloopvar
    # 前往 linter 检查错误处理表达式。	
    - err113
    # 检查枚举 switch 语句的详尽性。	
    - exhaustive
    # 检测长函数的工具。	
    - funlen
    # 检查是否存在全局变量。	
    - gochecknoglobals
    # 检查 Go 代码中不存在 init 函数。	
    - gochecknoinits
    # 提供诊断功能,检查错误、性能和样式问题。通过动态规则无需重新编译即可扩展。动态规则以声明方式编写,包含 AST 模式、过滤器、报告消息和可选建议。
    #- gocritic
    # 检查源代码是否存在安全问题。	
    - gosec
    # 强制使用一致的导入别名。	
    - importas
    # Maintidx测量每个功能的可维护性指数。	
    - maintidx
    # 报告深度嵌套的 if 语句。	
    - nestif
    # 检查 fmt.Sprintf 是否可以被更快的替代方案取代。	
    - perfsprint
    # 查找浪费的赋值语句。	
    - wastedassign


    # - nilaway

  # Run only fast linters from enabled linters set (first run won't be fast)
  # Default: false
  fast: true

linters-settings:
  gocyclo:
    # Minimal code complexity to report.
    # Default: 30 (but we recommend 10-20)
    min-complexity: 10

  wrapcheck:
    # An array of strings that specify substrings of signatures to ignore.
    # If this set, it will override the default set of ignored signatures.
    # See https://github.com/tomarrell/wrapcheck#configuration for more information.
    # Default: [".Errorf(", "errors.New(", "errors.Unwrap(", "errors.Join(", ".Wrap(", ".Wrapf(", ".WithMessage(", ".WithMessagef(", ".WithStack("]
    ignoreSigs:
      - .Errorf(
      - errors.New(
      - errors.Unwrap(
      - errors.Join(
      - .Wrap(
      - .Wrapf(
      - .WithMessage(
      - .WithMessagef(
      - .WithStack(
    # An array of strings that specify regular expressions of signatures to ignore.
    # Default: []
    ignoreSigRegexps:
      - \.New.*Error\(
    # An array of strings that specify globs of packages to ignore.
    # Default: []
    ignorePackageGlobs:
      - encoding/*
      - github.com/pkg/*
    # An array of strings that specify regular expressions of interfaces to ignore.
    # Default: []
    ignoreInterfaceRegexps:
      - ^(?i)c(?-i)ach(ing|e)

  gosec:
    # To select a subset of rules to run.
    # Available rules: https://github.com/securego/gosec#available-rules
    # Default: [] - means include all rules
    includes:
      - G101 # Look for hard coded credentials
      - G102 # Bind to all interfaces
      - G103 # Audit the use of unsafe block
      - G104 # Audit errors not checked
      - G106 # Audit the use of ssh.InsecureIgnoreHostKey
      - G107 # Url provided to HTTP request as taint input
      - G108 # Profiling endpoint automatically exposed on /debug/pprof
      - G109 # Potential Integer overflow made by strconv.Atoi result conversion to int16/32
      - G110 # Potential DoS vulnerability via decompression bomb
      - G111 # Potential directory traversal
      - G112 # Potential slowloris attack
      - G113 # Usage of Rat.SetString in math/big with an overflow (CVE-2022-23772)
      - G114 # Use of net/http serve function that has no support for setting timeouts
      - G115 # Potential integer overflow when converting between integer types
      - G201 # SQL query construction using format string
      - G202 # SQL query construction using string concatenation
      - G203 # Use of unescaped data in HTML templates
      - G204 # Audit use of command execution
      - G301 # Poor file permissions used when creating a directory
      - G302 # Poor file permissions used with chmod
      - G303 # Creating tempfile using a predictable path
      - G304 # File path provided as taint input
      - G305 # File traversal when extracting zip/tar archive
      - G306 # Poor file permissions used when writing to a new file
      - G307 # Poor file permissions used when creating a file with os.Create
      - G401 # Detect the usage of MD5 or SHA1
      - G402 # Look for bad TLS connection settings
      - G403 # Ensure minimum RSA key length of 2048 bits
      - G404 # Insecure random number source (rand)
      - G405 # Detect the usage of DES or RC4
      - G406 # Detect the usage of MD4 or RIPEMD160
      - G501 # Import blocklist: crypto/md5
      - G502 # Import blocklist: crypto/des
      - G503 # Import blocklist: crypto/rc4
      - G504 # Import blocklist: net/http/cgi
      - G505 # Import blocklist: crypto/sha1
      - G506 # Import blocklist: golang.org/x/crypto/md4
      - G507 #Import blocklist: golang.org/x/crypto/ripemd160
      - G601 # Implicit memory aliasing of items from a range statement
      - G602 # Slice access out of bounds
    # To specify a set of rules to explicitly exclude.
    # Available rules: https://github.com/securego/gosec#available-rules
    # Default: []
    excludes:
      - G101 # Look for hard coded credentials
      - G102 # Bind to all interfaces
      - G103 # Audit the use of unsafe block
      - G104 # Audit errors not checked
      - G106 # Audit the use of ssh.InsecureIgnoreHostKey
      - G107 # Url provided to HTTP request as taint input
      - G108 # Profiling endpoint automatically exposed on /debug/pprof
      - G109 # Potential Integer overflow made by strconv.Atoi result conversion to int16/32
      - G110 # Potential DoS vulnerability via decompression bomb
      - G111 # Potential directory traversal
      - G112 # Potential slowloris attack
      - G113 # Usage of Rat.SetString in math/big with an overflow (CVE-2022-23772)
      - G114 # Use of net/http serve function that has no support for setting timeouts
      - G115 # Potential integer overflow when converting between integer types
      - G201 # SQL query construction using format string
      - G202 # SQL query construction using string concatenation
      - G203 # Use of unescaped data in HTML templates
      - G204 # Audit use of command execution
      - G301 # Poor file permissions used when creating a directory
      - G302 # Poor file permissions used with chmod
      - G303 # Creating tempfile using a predictable path
      - G304 # File path provided as taint input
      - G305 # File traversal when extracting zip/tar archive
      - G306 # Poor file permissions used when writing to a new file
      - G307 # Poor file permissions used when creating a file with os.Create
      - G401 # Detect the usage of MD5 or SHA1
      - G402 # Look for bad TLS connection settings
      - G403 # Ensure minimum RSA key length of 2048 bits
      - G404 # Insecure random number source (rand)
      - G405 # Detect the usage of DES or RC4
      - G406 # Detect the usage of MD4 or RIPEMD160
      - G501 # Import blocklist: crypto/md5
      - G502 # Import blocklist: crypto/des
      - G503 # Import blocklist: crypto/rc4
      - G504 # Import blocklist: net/http/cgi
      - G505 # Import blocklist: crypto/sha1
      - G506 # Import blocklist: golang.org/x/crypto/md4
      - G507 #Import blocklist: golang.org/x/crypto/ripemd160
      - G601 # Implicit memory aliasing of items from a range statement
      - G602 # Slice access out of bounds
    # Exclude generated files
    # Default: false
    exclude-generated: true
    # Filter out the issues with a lower severity than the given value.
    # Valid options are: low, medium, high.
    # Default: low
    severity: medium
    # Filter out the issues with a lower confidence than the given value.
    # Valid options are: low, medium, high.
    # Default: low
    confidence: medium
    # Concurrency value.
    # Default: the number of logical CPUs usable by the current process.
    concurrency: 12
    # To specify the configuration of rules.
    config:
      # Globals are applicable to all rules.
      global:
        # If true, ignore #nosec in comments (and an alternative as well).
        # Default: false
        nosec: true
        # Add an alternative comment prefix to #nosec (both will work at the same time).
        # Default: ""
        "#nosec": "#my-custom-nosec"
        # Define whether nosec issues are counted as finding or not.
        # Default: false
        show-ignored: true
        # Audit mode enables addition checks that for normal code analysis might be too nosy.
        # Default: false
        audit: true
      G101:
        # Regexp pattern for variables and constants to find.
        # Default: "(?i)passwd|pass|password|pwd|secret|token|pw|apiKey|bearer|cred"
        pattern: "(?i)example"
        # If true, complain about all cases (even with low entropy).
        # Default: false
        ignore_entropy: false
        # Maximum allowed entropy of the string.
        # Default: "80.0"
        entropy_threshold: "80.0"
        # Maximum allowed value of entropy/string length.
        # Is taken into account if entropy >= entropy_threshold/2.
        # Default: "3.0"
        per_char_threshold: "3.0"
        # Calculate entropy for first N chars of the string.
        # Default: "16"
        truncate: "32"
      # Additional functions to ignore while checking unhandled errors.
      # Following functions always ignored:
      #   bytes.Buffer:
      #     - Write
      #     - WriteByte
      #     - WriteRune
      #     - WriteString
      #   fmt:
      #     - Print
      #     - Printf
      #     - Println
      #     - Fprint
      #     - Fprintf
      #     - Fprintln
      #   strings.Builder:
      #     - Write
      #     - WriteByte
      #     - WriteRune
      #     - WriteString
      #   io.PipeWriter:
      #     - CloseWithError
      #   hash.Hash:
      #     - Write
      #   os:
      #     - Unsetenv
      # Default: {}
      G104:
        fmt:
          - Fscanf
      G111:
        # Regexp pattern to find potential directory traversal.
        # Default: "http\\.Dir\\(\"\\/\"\\)|http\\.Dir\\('\\/'\\)"
        pattern: "custom\\.Dir\\(\\)"
      # Maximum allowed permissions mode for os.Mkdir and os.MkdirAll
      # Default: "0750"
      G301: "0750"
      # Maximum allowed permissions mode for os.OpenFile and os.Chmod
      # Default: "0600"
      G302: "0600"
      # Maximum allowed permissions mode for os.WriteFile and ioutil.WriteFile
      # Default: "0600"
      G306: "0600"

  # gocritic:
  #   # Disable all checks.
  #   # Default: false
  #   #disable-all: true
  #   # Which checks should be enabled in addition to default checks; can't be combined with 'disabled-checks'.
  #   # By default, list of stable checks is used (https://go-critic.github.io/overview#checks-overview):
  #   #   appendAssign, argOrder, assignOp, badCall, badCond, captLocal, caseOrder, codegenComment, commentFormatting,
  #   #   defaultCaseOrder, deprecatedComment, dupArg, dupBranchBody, dupCase, dupSubExpr, elseif, exitAfterDefer,
  #   #   flagDeref, flagName, ifElseChain, mapKey, newDeref, offBy1, regexpMust, singleCaseSwitch, sloppyLen,
  #   #   sloppyTypeAssert, switchTrue, typeSwitchVar, underef, unlambda, unslice, valSwap, wrapperFunc
  #   # To see which checks are enabled run `GL_DEBUG=gocritic golangci-lint run --enable=gocritic`.
  #   #enabled-checks:
  #     #- nestingReduce
  #     #- unnamedResult
  #     #- ruleguard
  #     #- truncateCmp
  #   # Enable all checks.
  #   # Default: false
  #   enable-all: true
  #   # Which checks should be disabled; can't be combined with 'enabled-checks'.
  #   # Default: []
  #   #disabled-checks:
  #   #  - regexpMust
  #   # Enable multiple checks by tags in addition to default checks.
  #   # Run `GL_DEBUG=gocritic golangci-lint run --enable=gocritic` to see all tags and checks.
  #   # See https://github.com/go-critic/go-critic#usage -> section "Tags".
  #   # Default: []
  #   #enabled-tags:
  #     #- diagnostic
  #     #- style
  #     #- performance
  #     #- experimental
  #     #- opinionated
  #   disabled-tags:
  #     - diagnostic
  #     - style
  #     - performance
  #     - experimental
  #     - opinionated
  #   # Settings passed to gocritic.
  #   # The settings key is the name of a supported gocritic checker.
  #   # The list of supported checkers can be find in https://go-critic.github.io/overview.
  #   settings:
  #     # Must be valid enabled check name.
  #     captLocal:
  #       # Whether to restrict checker to params only.
  #       # Default: true
  #       paramsOnly: false
  #     commentedOutCode:
  #       # Min length of the comment that triggers a warning.
  #       # Default: 15
  #       minLength: 50
  #     elseif:
  #       # Whether to skip balanced if-else pairs.
  #       # Default: true
  #       skipBalanced: false
  #     hugeParam:
  #       # Size in bytes that makes the warning trigger.
  #       # Default: 80
  #       sizeThreshold: 70
  #     ifElseChain:
  #       # Min number of if-else blocks that makes the warning trigger.
  #       # Default: 2
  #       minThreshold: 4
  #     nestingReduce:
  #       # Min number of statements inside a branch to trigger a warning.
  #       # Default: 5
  #       bodyWidth: 4
  #     rangeExprCopy:
  #       # Size in bytes that makes the warning trigger.
  #       # Default: 512
  #       sizeThreshold: 516
  #       # Whether to check test functions
  #       # Default: true
  #       skipTestFuncs: false
  #     rangeValCopy:
  #       # Size in bytes that makes the warning trigger.
  #       # Default: 128
  #       sizeThreshold: 32
  #       # Whether to check test functions.
  #       # Default: true
  #       skipTestFuncs: false
  #     ruleguard:
  #       # Enable debug to identify which 'Where' condition was rejected.
  #       # The value of the parameter is the name of a function in a ruleguard file.
  #       #
  #       # When a rule is evaluated:
  #       # If:
  #       #   The Match() clause is accepted; and
  #       #   One of the conditions in the Where() clause is rejected,
  #       # Then:
  #       #   ruleguard prints the specific Where() condition that was rejected.
  #       #
  #       # The option is passed to the ruleguard 'debug-group' argument.
  #       # Default: ""
  #       debug: 'emptyDecl'
  #       # Determines the behavior when an error occurs while parsing ruleguard files.
  #       # If flag is not set, log error and skip rule files that contain an error.
  #       # If flag is set, the value must be a comma-separated list of error conditions.
  #       # - 'all':    fail on all errors.
  #       # - 'import': ruleguard rule imports a package that cannot be found.
  #       # - 'dsl':    gorule file does not comply with the ruleguard DSL.
  #       # Default: ""
  #       failOn: dsl,import
  #       # Comma-separated list of file paths containing ruleguard rules.
  #       # If a path is relative, it is relative to the directory where the golangci-lint command is executed.
  #       # The special '${configDir}' variable is substituted with the absolute directory containing the golangci config file.
  #       # Glob patterns such as 'rules-*.go' may be specified.
  #       # Default: ""
  #       rules: '${configDir}/ruleguard/rules-*.go,${configDir}/myrule1.go'
  #       # Comma-separated list of enabled groups or skip empty to enable everything.
  #       # Tags can be defined with # character prefix.
  #       # Default: "<all>"
  #       enable: "myGroupName,#myTagName"
  #       # Comma-separated list of disabled groups or skip empty to enable everything.
  #       # Tags can be defined with # character prefix.
  #       # Default: ""
  #       disable: "myGroupName,#myTagName"
  #     tooManyResultsChecker:
  #       # Maximum number of results.
  #       # Default: 5
  #       maxResults: 10
  #     truncateCmp:
  #       # Whether to skip int/uint/uintptr types.
  #       # Default: true
  #       skipArchDependent: false
  #     underef:
  #       # Whether to skip (*x).method() calls where x is a pointer receiver.
  #       # Default: true
  #       skipRecvDeref: false
  #     unnamedResult:
  #       # Whether to check exported functions.
  #       # Default: false
  #       checkExported: true

  exhaustive:
    # Program elements to check for exhaustiveness.
    # Default: [ switch ]
    check:
      - switch
      - map
    # Check switch statements in generated files also.
    # Default: false
    check-generated: true
    # Presence of "default" case in switch statements satisfies exhaustiveness,
    # even if all enum members are not listed.
    # Default: false
    default-signifies-exhaustive: true
    # Enum members matching the supplied regex do not have to be listed in
    # switch statements to satisfy exhaustiveness.
    # Default: ""
    ignore-enum-members: "Example.+"
    # Enum types matching the supplied regex do not have to be listed in
    # switch statements to satisfy exhaustiveness.
    # Default: ""
    ignore-enum-types: "Example.+"
    # Consider enums only in package scopes, not in inner scopes.
    # Default: false
    package-scope-only: true
    # Only run exhaustive check on switches with "//exhaustive:enforce" comment.
    # Default: false
    explicit-exhaustive-switch: true
    # Only run exhaustive check on map literals with "//exhaustive:enforce" comment.
    # Default: false
    explicit-exhaustive-map: true
    # Switch statement requires default case even if exhaustive.
    # Default: false
    default-case-required: true        

  errchkjson:
    # With check-error-free-encoding set to true, errchkjson does warn about errors
    # from json encoding functions that are safe to be ignored,
    # because they are not possible to happen.
    #
    # if check-error-free-encoding is set to true and errcheck linter is enabled,
    # it is recommended to add the following exceptions to prevent from false positives:
    #
    #     linters-settings:
    #       errcheck:
    #         exclude-functions:
    #           - encoding/json.Marshal
    #           - encoding/json.MarshalIndent
    #
    # Default: false
    check-error-free-encoding: true
    # Issue on struct encoding that doesn't have exported fields.
    # Default: false
    report-no-exported: false

#   custom:
#     nilaway:
#       type: "module"
#       description: Static analysis tool to detect potential nil panics in Go code.
#       settings:
#         # Settings must be a "map from string to string" to mimic command line flags: the keys are
#         # flag names and the values are the values to the particular flags.
#         include-pkgs: "<YOUR_PACKAGE_PREFIXES>"
# # NilAway can be referred to as `nilaway` just like any other golangci-lint analyzers in other 
# # parts of the configuration file.    

settings.json文件

如果是远程开发,则修改的是远程IDE配置

默认为/root/.vscode-server/data/Machine/settings.json


vim /root/.vscode-server/data/Machine/settings.json

增加
    "go.lintTool": "golangci-lint",
    "go.lintFlags": [
        "--fast"
    ],

执行Lint Workspace

cmd+shift+p

image.png

即可展示相关err/warn

image.png

Nilaway

编译期间扫描nil空指针问题,避免大多数情况下的panic

github.com/uber-go/nil…

根据官网说明:暂时不支持直接嵌入分析驱动器成为Linter,只能单独运行

但另类方法是:使用 golangci-lint custom -v自动生成附带自定义model的二进制golangci-lint(详见方案2)

个人,还是推荐直接执行(方案1)

方案1直接执行扫描

以独立命令执行扫描


go install go.uber.org/nilaway/cmd/nilaway@latest

直接扫描空指针
nilaway ./...

方案2编译构造自定义golangci-lint

核心是,构建为包括自定义插件的二进制golangci-lint

生成的二进制可独立执行,但多次试验后发现暂时并不能作为VSCode中go.lintTool的默认工具

.custom-gcl.yml

在项目工程根目录,创建/修改.custom-gcl.yml 文件

vim .custom-gcl.yml 

version: v1.57.0
plugins:
  - module: "go.uber.org/nilaway"
    import: "go.uber.org/nilaway/cmd/gclplugin"
    version: latest # Or a fixed version for reproducible builds.

.golangci.yml

在项目工程根目录,创建/修改.golangci.yml 文件


vim .golangci.yml 

linters-settings:
   custom:
     nilaway:
       type: "module"
       description: Static analysis tool to detect potential nil panics in Go code.
       settings:
         # Settings must be a "map from string to string" to mimic command line flags: the keys are
         # flag names and the values are the values to the particular flags.
         include-pkgs: "<YOUR_PACKAGE_PREFIXES>"
 # NilAway can be referred to as `nilaway` just like any other golangci-lint analyzers in other 
 # parts of the configuration file.
 
     

关于golangci-lint插件系统介绍

golangci-lint.run/plugins/mod…


golangci-lint custom -v

会生成custom-gcl 二进制文件

执行./custom-gcl

生成后的二进制文件,如果成功在enable中,应该能看到nilaway被启用

./custom-gcl
和执行golangci-lint 结果差异在于包括了自定义的插件

./custom-gcl linters
会看到enable中增加了nilaway

./custom-gcl run
同原生./golangci-lint 执行效果一致