@@ -52,18 +52,21 @@ func appendclipped(pass *analysis.Pass) {
52
52
// Only appends whose base is a clipped slice can be simplified:
53
53
// We must conservatively assume an append to an unclipped slice
54
54
// such as append(y[:0], x...) is intended to have effects on y.
55
- clipped , empty := isClippedSlice (info , base )
56
- if ! clipped {
55
+ clipped , empty := clippedSlice (info , base )
56
+ if clipped == nil {
57
57
return
58
58
}
59
59
60
60
// If the (clipped) base is empty, it may be safely ignored.
61
- // Otherwise treat it as just another arg (the first) to Concat.
61
+ // Otherwise treat it (or its unclipped subexpression, if possible)
62
+ // as just another arg (the first) to Concat.
62
63
if ! empty {
63
- sliceArgs = append (sliceArgs , base )
64
+ sliceArgs = append (sliceArgs , clipped )
64
65
}
65
66
slices .Reverse (sliceArgs )
66
67
68
+ // TODO(adonovan): simplify sliceArgs[0] further: slices.Clone(s) -> s
69
+
67
70
// Concat of a single (non-trivial) slice degenerates to Clone.
68
71
if len (sliceArgs ) == 1 {
69
72
s := sliceArgs [0 ]
@@ -111,11 +114,6 @@ func appendclipped(pass *analysis.Pass) {
111
114
}
112
115
113
116
// append(append(append(base, a...), b..., c...) -> slices.Concat(base, a, b, c)
114
- //
115
- // TODO(adonovan): simplify sliceArgs[0] further:
116
- // - slices.Clone(s) -> s
117
- // - s[:len(s):len(s)] -> s
118
- // - slices.Clip(s) -> s
119
117
_ , prefix , importEdits := analysisinternal .AddImport (info , file , "slices" , "slices" , "Concat" , call .Pos ())
120
118
pass .Report (analysis.Diagnostic {
121
119
Pos : call .Pos (),
@@ -172,46 +170,57 @@ func appendclipped(pass *analysis.Pass) {
172
170
}
173
171
}
174
172
175
- // isClippedSlice reports whether e denotes a slice that is definitely
176
- // clipped, that is, its len(s)==cap(s).
173
+ // clippedSlice returns res != nil if e denotes a slice that is
174
+ // definitely clipped, that is, its len(s)==cap(s).
175
+ //
176
+ // The value of res is either the same as e or is a subexpression of e
177
+ // that denotes the same slice but without the clipping operation.
177
178
//
178
- // In addition, it reports whether the slice is definitely empty.
179
+ // In addition, it reports whether the slice is definitely empty,
179
180
//
180
181
// Examples of clipped slices:
181
182
//
182
183
// x[:0:0] (empty)
183
184
// []T(nil) (empty)
184
185
// Slice{} (empty)
185
- // x[:len(x):len(x)] (nonempty)
186
+ // x[:len(x):len(x)] (nonempty) res=x
186
187
// x[:k:k] (nonempty)
187
- // slices.Clip(x) (nonempty)
188
- func isClippedSlice (info * types.Info , e ast.Expr ) (clipped , empty bool ) {
188
+ // slices.Clip(x) (nonempty) res=x
189
+ func clippedSlice (info * types.Info , e ast.Expr ) (res ast. Expr , empty bool ) {
189
190
switch e := e .(type ) {
190
191
case * ast.SliceExpr :
191
- // x[:0:0], x[:len(x):len(x)], x[:k:k], x[:0]
192
- clipped = e .Slice3 && e .High != nil && e .Max != nil && equalSyntax (e .High , e .Max ) // x[:k:k]
193
- empty = e .High != nil && isZeroLiteral (e .High ) // x[:0:*]
192
+ // x[:0:0], x[:len(x):len(x)], x[:k:k]
193
+ if e .Slice3 && e .High != nil && e .Max != nil && equalSyntax (e .High , e .Max ) { // x[:k:k]
194
+ res = e
195
+ empty = isZeroLiteral (e .High ) // x[:0:0]
196
+ if call , ok := e .High .(* ast.CallExpr ); ok &&
197
+ typeutil .Callee (info , call ) == builtinLen &&
198
+ equalSyntax (call .Args [0 ], e .X ) {
199
+ res = e .X // x[:len(x):len(x)] -> x
200
+ }
201
+ return
202
+ }
194
203
return
195
204
196
205
case * ast.CallExpr :
197
206
// []T(nil)?
198
207
if info .Types [e .Fun ].IsType () &&
199
208
is [* ast.Ident ](e .Args [0 ]) &&
200
209
info .Uses [e .Args [0 ].(* ast.Ident )] == builtinNil {
201
- return true , true
210
+ return e , true
202
211
}
203
212
204
213
// slices.Clip(x)?
205
214
obj := typeutil .Callee (info , e )
206
215
if analysisinternal .IsFunctionNamed (obj , "slices" , "Clip" ) {
207
- return true , false
216
+ return e . Args [ 0 ] , false // slices.Clip(x) -> x
208
217
}
209
218
210
219
case * ast.CompositeLit :
211
220
// Slice{}?
212
221
if len (e .Elts ) == 0 {
213
- return true , true
222
+ return e , true
214
223
}
215
224
}
216
- return false , false
225
+ return nil , false
217
226
}
0 commit comments