@@ -52,18 +52,21 @@ func appendclipped(pass *analysis.Pass) {
5252 // Only appends whose base is a clipped slice can be simplified:
5353 // We must conservatively assume an append to an unclipped slice
5454 // 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 {
5757 return
5858 }
5959
6060 // 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.
6263 if ! empty {
63- sliceArgs = append (sliceArgs , base )
64+ sliceArgs = append (sliceArgs , clipped )
6465 }
6566 slices .Reverse (sliceArgs )
6667
68+ // TODO(adonovan): simplify sliceArgs[0] further: slices.Clone(s) -> s
69+
6770 // Concat of a single (non-trivial) slice degenerates to Clone.
6871 if len (sliceArgs ) == 1 {
6972 s := sliceArgs [0 ]
@@ -111,11 +114,6 @@ func appendclipped(pass *analysis.Pass) {
111114 }
112115
113116 // 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
119117 _ , prefix , importEdits := analysisinternal .AddImport (info , file , "slices" , "slices" , "Concat" , call .Pos ())
120118 pass .Report (analysis.Diagnostic {
121119 Pos : call .Pos (),
@@ -172,46 +170,57 @@ func appendclipped(pass *analysis.Pass) {
172170 }
173171}
174172
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.
177178//
178- // In addition, it reports whether the slice is definitely empty.
179+ // In addition, it reports whether the slice is definitely empty,
179180//
180181// Examples of clipped slices:
181182//
182183// x[:0:0] (empty)
183184// []T(nil) (empty)
184185// Slice{} (empty)
185- // x[:len(x):len(x)] (nonempty)
186+ // x[:len(x):len(x)] (nonempty) res=x
186187// 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 ) {
189190 switch e := e .(type ) {
190191 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+ }
194203 return
195204
196205 case * ast.CallExpr :
197206 // []T(nil)?
198207 if info .Types [e .Fun ].IsType () &&
199208 is [* ast.Ident ](e .Args [0 ]) &&
200209 info .Uses [e .Args [0 ].(* ast.Ident )] == builtinNil {
201- return true , true
210+ return e , true
202211 }
203212
204213 // slices.Clip(x)?
205214 obj := typeutil .Callee (info , e )
206215 if analysisinternal .IsFunctionNamed (obj , "slices" , "Clip" ) {
207- return true , false
216+ return e . Args [ 0 ] , false // slices.Clip(x) -> x
208217 }
209218
210219 case * ast.CompositeLit :
211220 // Slice{}?
212221 if len (e .Elts ) == 0 {
213- return true , true
222+ return e , true
214223 }
215224 }
216- return false , false
225+ return nil , false
217226}
0 commit comments