PR: preserves comments in mapsloop

PR: golang/tools#660255 fixes Issue: golang/go#72958

How I found it

Hanging around in issues.

How to solve it

The solution is quite straight as the issue described. We can simplely collects the comments using allComments and put the comments before the target line.

As the allComments function is inside minmax, we ought to move it out. (Thanks for Alan Donovan reviewed and recommonded moved it out instead of simplely copied it.)

At first, I just collected all the comments and put it to start node no matter mrhs is nil or not. But it behaved wrong when mrhs != nil.

When mrhs != nil, the start node is located as the arrow indicates,

m := make(map[string]string)
 ^

So we would add the comments at wrong position, to correct it, we should used curPrev.Node(), which is the node before rng , instead of rng node.

So the final changes is,

--- a/gopls/internal/analysis/modernize/maps.go
+++ b/gopls/internal/analysis/modernize/maps.go
@@ -156,16 +156,35 @@ func mapsloop(pass *analysis.Pass) {
                        start, end token.Pos
                )
                if mrhs != nil {
-                       // Replace RHS of preceding m=... assignment (and loop) with expression.
-                       start, end = mrhs.Pos(), rng.End()
-                       newText = fmt.Appendf(nil, "%s%s(%s)",
+                       // Replace assignment and loop with expression.
+                       //
+                       //   m = make(...)
+                       //   for k, v := range x { /* comments */ m[k] = v }
+                       //
+                       //   ->
+                       //
+                       //   /* comments */
+                       //   m = maps.Copy(x)
+                       curPrev, _ := curRange.PrevSibling()
+                       start, end = curPrev.Node().Pos(), rng.End()
+                       newText = fmt.Appendf(nil, "%s%s = %s%s(%s)",
+                               allComments(file, start, end),
+                               analysisinternal.Format(pass.Fset, m),
                                prefix,
                                funcName,
                                analysisinternal.Format(pass.Fset, x))
                } else {
                        // Replace loop with call statement.
+                       //
+                       //   for k, v := range x { /* comments */ m[k] = v }
+                       //
+                       //   ->
+                       //
+                       //   /* comments */
+                       //   maps.Copy(m, x)
                        start, end = rng.Pos(), rng.End()
-                       newText = fmt.Appendf(nil, "%s%s(%s, %s)",
+                       newText = fmt.Appendf(nil, "%s%s%s(%s, %s)",
+                               allComments(file, start, end),
                                prefix,
                                funcName,
                                analysisinternal.Format(pass.Fset, m),

What I learnt

  1. Never repeat yourself.