@@ -78,6 +78,10 @@ type modData struct {
78
78
// upgrades is a map of path->version that contains any upgrades for the go.mod.
79
79
upgrades map [string ]string
80
80
81
+ // why is a map of path->explanation that contains all the "go mod why" contents
82
+ // for each require statement.
83
+ why map [string ]string
84
+
81
85
// parseErrors are the errors that arise when we diff between a user's go.mod
82
86
// and the "tidied" go.mod.
83
87
parseErrors []source.Error
@@ -112,6 +116,15 @@ func (mh *modHandle) Upgrades(ctx context.Context) (*modfile.File, *protocol.Col
112
116
return data .origParsedFile , data .origMapper , data .upgrades , data .err
113
117
}
114
118
119
+ func (mh * modHandle ) Why (ctx context.Context ) (* modfile.File , * protocol.ColumnMapper , map [string ]string , error ) {
120
+ v := mh .handle .Get (ctx )
121
+ if v == nil {
122
+ return nil , nil , nil , errors .Errorf ("no parsed file for %s" , mh .File ().Identity ().URI )
123
+ }
124
+ data := v .(* modData )
125
+ return data .origParsedFile , data .origMapper , data .why , data .err
126
+ }
127
+
115
128
func (s * snapshot ) ModHandle (ctx context.Context , fh source.FileHandle ) source.ModHandle {
116
129
uri := fh .Identity ().URI
117
130
if handle := s .getModHandle (uri ); handle != nil {
@@ -165,7 +178,15 @@ func (s *snapshot) ModHandle(ctx context.Context, fh source.FileHandle) source.M
165
178
}
166
179
}
167
180
// Only get dependency upgrades if the go.mod file is the same as the view's.
168
- data .upgrades , data .err = dependencyUpgrades (ctx , cfg , folder , data )
181
+ if err := dependencyUpgrades (ctx , cfg , folder , data ); err != nil {
182
+ data .err = err
183
+ return data
184
+ }
185
+ // Only run "go mod why" if the go.mod file is the same as the view's.
186
+ if err := goModWhy (ctx , cfg , folder , data ); err != nil {
187
+ data .err = err
188
+ return data
189
+ }
169
190
return data
170
191
})
171
192
s .mu .Lock ()
@@ -178,9 +199,39 @@ func (s *snapshot) ModHandle(ctx context.Context, fh source.FileHandle) source.M
178
199
return s .modHandles [uri ]
179
200
}
180
201
181
- func dependencyUpgrades (ctx context.Context , cfg * packages.Config , folder string , data * modData ) (map [string ]string , error ) {
202
+ func goModWhy (ctx context.Context , cfg * packages.Config , folder string , data * modData ) error {
203
+ if len (data .origParsedFile .Require ) == 0 {
204
+ return nil
205
+ }
206
+ // Run "go mod why" on all the dependencies to get information about the usages.
207
+ inv := gocommand.Invocation {
208
+ Verb : "mod" ,
209
+ Args : []string {"why" , "-m" },
210
+ BuildFlags : cfg .BuildFlags ,
211
+ Env : cfg .Env ,
212
+ WorkingDir : folder ,
213
+ }
214
+ for _ , req := range data .origParsedFile .Require {
215
+ inv .Args = append (inv .Args , req .Mod .Path )
216
+ }
217
+ stdout , err := inv .Run (ctx )
218
+ if err != nil {
219
+ return err
220
+ }
221
+ whyList := strings .Split (stdout .String (), "\n \n " )
222
+ if len (whyList ) <= 1 || len (whyList ) > len (data .origParsedFile .Require ) {
223
+ return nil
224
+ }
225
+ data .why = make (map [string ]string )
226
+ for i , req := range data .origParsedFile .Require {
227
+ data .why [req .Mod .Path ] = whyList [i ]
228
+ }
229
+ return nil
230
+ }
231
+
232
+ func dependencyUpgrades (ctx context.Context , cfg * packages.Config , folder string , data * modData ) error {
182
233
if len (data .origParsedFile .Require ) == 0 {
183
- return nil , nil
234
+ return nil
184
235
}
185
236
// Run "go list -u -m all" to be able to see which deps can be upgraded.
186
237
inv := gocommand.Invocation {
@@ -192,13 +243,13 @@ func dependencyUpgrades(ctx context.Context, cfg *packages.Config, folder string
192
243
}
193
244
stdout , err := inv .Run (ctx )
194
245
if err != nil {
195
- return nil , err
246
+ return err
196
247
}
197
248
upgradesList := strings .Split (stdout .String (), "\n " )
198
249
if len (upgradesList ) <= 1 {
199
- return nil , nil
250
+ return nil
200
251
}
201
- upgrades : = make (map [string ]string )
252
+ data . upgrades = make (map [string ]string )
202
253
for _ , upgrade := range upgradesList [1 :] {
203
254
// Example: "github.com/x/tools v1.1.0 [v1.2.0]"
204
255
info := strings .Split (upgrade , " " )
@@ -208,9 +259,9 @@ func dependencyUpgrades(ctx context.Context, cfg *packages.Config, folder string
208
259
dep , version := info [0 ], info [2 ]
209
260
latest := version [1 :] // remove the "["
210
261
latest = strings .TrimSuffix (latest , "]" ) // remove the "]"
211
- upgrades [dep ] = latest
262
+ data . upgrades [dep ] = latest
212
263
}
213
- return upgrades , nil
264
+ return nil
214
265
}
215
266
216
267
func (mh * modHandle ) Tidy (ctx context.Context ) (* modfile.File , * protocol.ColumnMapper , map [string ]* modfile.Require , []source.Error , error ) {
0 commit comments