@@ -80,6 +80,17 @@ func checkNilnesserr(pass *analysis.Pass, b *ssa.BasicBlock, errors []errFact, i
8080 })
8181 }
8282 }
83+
84+ // extra check for variadic arguments
85+ variadicArgs := checkVariadicCall (instr )
86+ for _ , value := range variadicArgs {
87+ if checkSSAValue (value , errors , isNilnees ) {
88+ pass .Report (analysis.Diagnostic {
89+ Pos : pos ,
90+ Message : linterCall2Message ,
91+ })
92+ }
93+ }
8394 }
8495 }
8596}
@@ -93,3 +104,89 @@ func checkSSAValue(res ssa.Value, errors []errFact, isNilnees func(value ssa.Val
93104
94105 return lastValue != nil
95106}
107+
108+ func checkVariadicCall (call * ssa.Call ) []ssa.Value {
109+ alloc := validateVariadicCall (call )
110+ if alloc == nil {
111+ return nil
112+ }
113+
114+ return extractVariadicErrors (alloc )
115+ }
116+
117+ /*
118+ example: fmt.Errorf("call Do2 got err %w", err)
119+
120+ type *ssa.Alloc instr new [1]any (varargs)
121+ type *ssa.IndexAddr instr &t4[0:int]
122+ type *ssa.ChangeInterface instr change interface any <- error (t0)
123+ type *ssa.Store instr *t5 = t6
124+ ...
125+ type *ssa.Slice instr slice t4[:]
126+ type *ssa.Call instr fmt.Errorf("call Do2 got err ...":string, t7...)
127+ */
128+ func validateVariadicCall (call * ssa.Call ) * ssa.Alloc {
129+ fn , ok := call .Call .Value .(* ssa.Function )
130+ if ! ok {
131+ return nil
132+ }
133+ if ! fn .Signature .Variadic () {
134+ return nil
135+ }
136+
137+ if len (call .Call .Args ) == 0 {
138+ return nil
139+ }
140+ lastArg := call .Call .Args [len (call .Call .Args )- 1 ]
141+ slice , ok := lastArg .(* ssa.Slice )
142+ if ! ok {
143+ return nil
144+ }
145+ // check is t[:]
146+ if ! (slice .Low == nil && slice .High == nil && slice .Max == nil ) {
147+ return nil
148+ }
149+ alloc , ok := slice .X .(* ssa.Alloc )
150+ if ! ok {
151+ return nil
152+ }
153+ valueType , ok := alloc .Type ().(* types.Pointer )
154+ if ! ok {
155+ return nil
156+ }
157+
158+ // check is array
159+ _ , ok = valueType .Elem ().(* types.Array )
160+ if ! ok {
161+ return nil
162+ }
163+
164+ return alloc
165+ }
166+
167+ // the Referrer chain is like this.
168+ // Alloc --> IndexAddr --> ChangeInterface --> Store ---> Slice.
169+ // Alloc --> IndexAddr --> Store --> Slice.
170+ func extractVariadicErrors (alloc * ssa.Alloc ) []ssa.Value {
171+ values := make ([]ssa.Value , 0 )
172+
173+ for _ , instr := range * alloc .Referrers () {
174+ indexAddr , ok := instr .(* ssa.IndexAddr )
175+ if ! ok {
176+ continue
177+ }
178+ for _ , instr2 := range * indexAddr .Referrers () {
179+ store , ok := instr2 .(* ssa.Store )
180+ if ! ok {
181+ continue
182+ }
183+ value := store .Val
184+ if change , ok := value .(* ssa.ChangeInterface ); ok {
185+ value = change .X
186+ }
187+ values = append (values , value )
188+ }
189+ }
190+
191+ return values
192+ }
0 commit comments