Skip to content

Combine checks and trims with CutPrefix or CutSuffix Has Fix

Description

Since Go 1.20, strings.CutPrefix and strings.CutSuffix return both the remaining string and whether the affix matched. This rule replaces an adjacent check and trim only when they use the same simple value and affix. It also skips bodies containing ok, because the fix introduces that name in the if initializer.

The rule assumes strings is the standard-library package imported under its canonical name and does not add the import.

YAML

yaml
id: use-strings-cut-prefix-suffix
language: Go
severity: hint
message: Combine an affix check and trim with strings.CutPrefix or strings.CutSuffix.
rule:
  all:
    - pattern:
        context: |
          func f() {
            if $_CHECK($$$CHECK_ARGS) {
              $REST := $TRIM_CALL
              $$$BODY
            }
          }
        selector: if_statement
    - any:
        - pattern:
            context: |
              func f() {
                if strings.HasPrefix($VALUE, $AFFIX) {
                  $REST := strings.TrimPrefix($VALUE, $AFFIX)
                  $$$BODY
                }
              }
            selector: if_statement
        - pattern:
            context: |
              func f() {
                if strings.HasSuffix($VALUE, $AFFIX) {
                  $REST := strings.TrimSuffix($VALUE, $AFFIX)
                  $$$BODY
                }
              }
            selector: if_statement
    - not:
        regex: '\bok\b'
constraints:
  VALUE:
    kind: identifier
  AFFIX:
    any:
      - kind: identifier
      - kind: interpreted_string_literal
      - kind: raw_string_literal
transform:
  CUT_CALL:
    replace:
      source: $TRIM_CALL
      replace: '^strings\.Trim'
      by: strings.Cut
fix: |-
  if $REST, ok := $CUT_CALL; ok {
    $$$BODY
  }

Example

go
import "strings"

func inspect(name string) {
  if strings.HasPrefix(name, "log:") {
    value := strings.TrimPrefix(name, "log:")
    use(value)
  }
  if strings.HasSuffix(name, ".tmp") {
    value := strings.TrimSuffix(name, ".tmp")
    use(value)
  }
}

Diff

go
import "strings"

func inspect(name string) {
  if strings.HasPrefix(name, "log:") { 
    value := strings.TrimPrefix(name, "log:") 
  if value, ok := strings.CutPrefix(name, "log:"); ok { 
    use(value)
  }
  if strings.HasSuffix(name, ".tmp") { 
    value := strings.TrimSuffix(name, ".tmp") 
  if value, ok := strings.CutSuffix(name, ".tmp"); ok { 
    use(value)
  }
}

Credits

Based on JetBrains' Go Modern Guidelines.

Made with ❤️ with Rust