Skip to content

Commit 7ef9e7d

Browse files
dsnetdominikh
authored andcommitted
staticcheck: recognize more options in JSON tags
These are supported by a prototype implementation of json. Closes: gh-1266 [via git-merge-pr] (cherry picked from commit 003d277)
1 parent 329c593 commit 7ef9e7d

File tree

3 files changed

+22
-18
lines changed

3 files changed

+22
-18
lines changed

staticcheck/lint.go

+17-13
Original file line numberDiff line numberDiff line change
@@ -3892,22 +3892,23 @@ func checkJSONTag(pass *analysis.Pass, field *ast.Field, tag string) {
38923892
//lint:ignore SA9003 TODO(dh): should we flag empty tags?
38933893
if len(tag) == 0 {
38943894
}
3895+
if i := strings.Index(tag, ",format:"); i >= 0 {
3896+
tag = tag[:i]
3897+
}
38953898
fields := strings.Split(tag, ",")
38963899
for _, r := range fields[0] {
38973900
if !unicode.IsLetter(r) && !unicode.IsDigit(r) && !strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", r) {
38983901
report.Report(pass, field.Tag, fmt.Sprintf("invalid JSON field name %q", fields[0]))
38993902
}
39003903
}
3901-
var co, cs, ci int
3904+
options := make(map[string]int)
39023905
for _, s := range fields[1:] {
39033906
switch s {
3904-
case "omitempty":
3905-
co++
39063907
case "":
39073908
// allow stuff like "-,"
39083909
case "string":
3909-
cs++
39103910
// only for string, floating point, integer and bool
3911+
options[s]++
39113912
tset := typeutil.NewTypeSet(pass.TypesInfo.TypeOf(field.Type))
39123913
if len(tset.Terms) == 0 {
39133914
// TODO(dh): improve message, call out the use of type parameters
@@ -3924,20 +3925,23 @@ func checkJSONTag(pass *analysis.Pass, field *ast.Field, tag string) {
39243925
}
39253926
}
39263927
}
3927-
case "inline":
3928-
ci++
3928+
case "omitzero", "omitempty", "nocase", "inline", "unknown":
3929+
options[s]++
39293930
default:
39303931
report.Report(pass, field.Tag, fmt.Sprintf("unknown JSON option %q", s))
39313932
}
39323933
}
3933-
if co > 1 {
3934-
report.Report(pass, field.Tag, `duplicate JSON option "omitempty"`)
3935-
}
3936-
if cs > 1 {
3937-
report.Report(pass, field.Tag, `duplicate JSON option "string"`)
3934+
var duplicates []string
3935+
for option, n := range options {
3936+
if n > 1 {
3937+
duplicates = append(duplicates, option)
3938+
}
39383939
}
3939-
if ci > 1 {
3940-
report.Report(pass, field.Tag, `duplicate JSON option "inline"`)
3940+
if len(duplicates) > 0 {
3941+
sort.Strings(duplicates)
3942+
for _, option := range duplicates {
3943+
report.Report(pass, field.Tag, fmt.Sprintf("duplicate JSON option %q", option))
3944+
}
39413945
}
39423946
}
39433947

staticcheck/testdata/src/CheckStructTags/CheckStructTags.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ type T1 struct {
1414
F int `json:",omitempty,omitempty"` //@ diag(`duplicate JSON option "omitempty"`)
1515
G int `json:",omitempty,string"`
1616
H int `json:",string,omitempty,string"` //@ diag(`duplicate JSON option "string"`)
17-
I int `json:",unknown"` //@ diag(`unknown JSON option "unknown"`)
17+
I int `json:",foreign"` //@ diag(`unknown JSON option "foreign"`)
1818
J int `json:",string"`
1919
K *int `json:",string"`
2020
L **int `json:",string"` //@ diag(`the JSON string option`)
2121
M complex128 `json:",string"` //@ diag(`the JSON string option`)
2222
N int `json:"some-name"`
23-
O int `json:"some-name,inline"`
23+
O int `json:"some-name,omitzero,omitempty,nocase,inline,unknown,format:'something,with,commas'"`
2424
}
2525

2626
type T2 struct {
@@ -38,7 +38,7 @@ type T2 struct {
3838

3939
type T3 struct {
4040
A int `json:",omitempty" xml:",attr"`
41-
B int `json:",unknown" xml:",attr"` //@ diag(`unknown JSON option`)
41+
B int `json:",foreign" xml:",attr"` //@ diag(`unknown JSON option "foreign"`)
4242
}
4343

4444
type T4 struct {

staticcheck/testdata/src/CheckStructTags3/CheckStructTags.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type T1 struct {
1010
F int `json:",omitempty,omitempty"` //@ diag(`duplicate JSON option "omitempty"`)
1111
G int `json:",omitempty,string"`
1212
H int `json:",string,omitempty,string"` //@ diag(`duplicate JSON option "string"`)
13-
I int `json:",unknown"` //@ diag(`unknown JSON option "unknown"`)
13+
I int `json:",foreign"` //@ diag(`unknown JSON option "foreign"`)
1414
J int `json:",string"`
1515
K *int `json:",string"`
1616
L **int `json:",string"` //@ diag(`the JSON string option`)
@@ -34,7 +34,7 @@ type T2 struct {
3434

3535
type T3 struct {
3636
A int `json:",omitempty" xml:",attr"`
37-
B int `json:",unknown" xml:",attr"` //@ diag(`unknown JSON option`)
37+
B int `json:",foreign" xml:",attr"` //@ diag(`unknown JSON option "foreign"`)
3838
}
3939

4040
type T4 struct {

0 commit comments

Comments
 (0)