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.