Skip to content

Commit ca27ae0

Browse files
committed
SA1019: Fix missing deprecated fields in struct initialization
1 parent a367d57 commit ca27ae0

File tree

3 files changed

+92
-23
lines changed

3 files changed

+92
-23
lines changed

staticcheck/sa1019/sa1019.go

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
5050
return !strings.Contains(path, ".")
5151
}
5252

53-
handleDeprecation := func(depr *deprecated.IsDeprecated, node ast.Node, deprecatedObjName string, pkgPath string, tfn types.Object) {
53+
handleDeprecation := func(depr *deprecated.IsDeprecated, node ast.Node, deprecatedObjName string, pkgPath string, tfn types.Object, nodeRender func() string) {
5454
std, ok := knowledge.StdlibDeprecations[deprecatedObjName]
5555
if !ok && isStdlibPath(pkgPath) {
5656
// Deprecated object in the standard library, but we don't know the details of the deprecation.
@@ -105,32 +105,20 @@ func run(pass *analysis.Pass) (interface{}, error) {
105105
report.Render(pass, node), std.DeprecatedSince, std.AlternativeAvailableSince, depr.Msg))
106106
}
107107
} else {
108-
report.Report(pass, node, fmt.Sprintf("%s is deprecated: %s", report.Render(pass, node), depr.Msg))
108+
report.Report(pass, node, fmt.Sprintf("%s is deprecated: %s", nodeRender(), depr.Msg))
109109
}
110110
}
111111

112112
var tfn types.Object
113113
stack := 0
114-
fn := func(node ast.Node, push bool) bool {
115-
if !push {
116-
stack--
117-
return false
118-
}
119-
stack++
120-
if stack == 1 {
121-
tfn = nil
122-
}
123-
if fn, ok := node.(*ast.FuncDecl); ok {
124-
tfn = pass.TypesInfo.ObjectOf(fn.Name)
125-
}
126114

127-
// FIXME(dh): this misses dot-imported objects
128-
sel, ok := node.(*ast.SelectorExpr)
129-
if !ok {
130-
return true
131-
}
132-
133-
obj := pass.TypesInfo.ObjectOf(sel.Sel)
115+
checkIdentObj := func(
116+
node ast.Node,
117+
id *ast.Ident,
118+
obj types.Object,
119+
nameFunc func() string,
120+
nodeRender func() string,
121+
) bool {
134122
if obj_, ok := obj.(*types.Func); ok {
135123
obj = obj_.Origin()
136124
}
@@ -161,7 +149,60 @@ func run(pass *analysis.Pass) (interface{}, error) {
161149
}
162150

163151
if depr, ok := deprs.Objects[obj]; ok {
164-
handleDeprecation(depr, sel, code.SelectorName(pass, sel), obj.Pkg().Path(), tfn)
152+
handleDeprecation(depr, node, nameFunc(), obj.Pkg().Path(), tfn, nodeRender)
153+
}
154+
return true
155+
}
156+
157+
fn := func(node ast.Node, push bool) bool {
158+
if !push {
159+
stack--
160+
return false
161+
}
162+
stack++
163+
if stack == 1 {
164+
tfn = nil
165+
}
166+
if fn, ok := node.(*ast.FuncDecl); ok {
167+
tfn = pass.TypesInfo.ObjectOf(fn.Name)
168+
}
169+
170+
switch v := node.(type) {
171+
// FIXME(dh): this misses dot-imported objects
172+
case *ast.SelectorExpr:
173+
sel := v
174+
obj := pass.TypesInfo.ObjectOf(sel.Sel)
175+
return checkIdentObj(sel, sel.Sel, obj, func() string {
176+
return code.SelectorName(pass, sel)
177+
}, func() string {
178+
return report.Render(pass, sel)
179+
})
180+
181+
case *ast.CompositeLit:
182+
for _, elt := range v.Elts {
183+
kv, ok := elt.(*ast.KeyValueExpr)
184+
if !ok {
185+
return true
186+
}
187+
key, ok := kv.Key.(*ast.Ident)
188+
// ast.KeyValueExpr also represents key value pairs in maps, where the `Key` can be a *ast.BasicLit
189+
if !ok {
190+
return true
191+
}
192+
obj := pass.TypesInfo.ObjectOf(key)
193+
checkIdentObj(key, key, obj, func() string {
194+
return key.Name
195+
}, func() string {
196+
if se, ok := v.Type.(*ast.SelectorExpr); ok {
197+
if xI, ok := se.X.(*ast.Ident); ok {
198+
return fmt.Sprintf("%s.%s.%s", xI.Name, se.Sel.Name, key.Name)
199+
} else {
200+
return fmt.Sprintf("%s.%s", se.Sel.Name, key.Name)
201+
}
202+
}
203+
return key.Name
204+
})
205+
}
165206
}
166207
return true
167208
}
@@ -198,7 +239,9 @@ func run(pass *analysis.Pass) (interface{}, error) {
198239
return
199240
}
200241

201-
handleDeprecation(depr, spec.Path, path, path, nil)
242+
handleDeprecation(depr, spec.Path, path, path, nil, func() string {
243+
return report.Render(pass, node)
244+
})
202245
}
203246
}
204247
pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes(nil, fn)

staticcheck/sa1019/testdata/src/example.com/CheckDeprecated/CheckDeprecated.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import _ "example.com/CheckDeprecated.assist" //@ diag(`Alas, it is dep
55
import _ "example.com/AnotherCheckDeprecated.assist" //@ diag(`Alas, it is deprecated.`)
66
import foo "example.com/AnotherCheckDeprecated.assist" //@ diag(`Alas, it is deprecated.`)
77
import "example.com/AnotherCheckDeprecated.assist" //@ diag(`Alas, it is deprecated.`)
8+
import ae "example.com/CheckDeprecated.assist_external"
89

910
func init() {
1011
foo.Fn()
@@ -13,6 +14,20 @@ func init() {
1314
// Field is deprecated, but we're using it from the same package, which is fine.
1415
var s S
1516
_ = s.Field
17+
18+
s2 := ae.SD{
19+
D: "used", //@ diag(`external don't use me`)
20+
}
21+
_ = s2
22+
// Struct with the same name should not be flagged
23+
s3 := ae.SN{
24+
D:"also",
25+
}
26+
_ = s3
27+
// Other Key-Value expressions should be safely ignored
28+
_ = map[string]string {
29+
"left":"right",
30+
}
1631
}
1732

1833

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package pkg
2+
3+
type SD struct {
4+
// Deprecated: external don't use me
5+
D string
6+
}
7+
8+
type SN struct {
9+
// Not deprecated, but named the same
10+
D string
11+
}

0 commit comments

Comments
 (0)