The modload package has a lot of global state. In general, variables that are set once during initialization and global caches are fine. But buildList and loader in particular cause problems, since most exported functions either read values out of them or replace and overwrite them. It's frequently not obvious when this is happening.
This leads to some unfortunate situations. For example, see get.go in CL 228383. If modload.ListModules is called before modload.WriteGoMod, the go.mod file will contain spurious // indirect comments. This happens because WriteGoMod uses loaded.direct to decide whether to mark a requirement as indirect, but ListModules calls LoadBuildList, which replaces loaded, so it appears that nothing is directly required.
I think these side effects should be more obvious to callers, and the loaded and buildList variables should be removed. Instead, the loader struct type should be exported, and exported functions that create, read, or write it should be converted to methods.
The
modloadpackage has a lot of global state. In general, variables that are set once during initialization and global caches are fine. ButbuildListandloaderin particular cause problems, since most exported functions either read values out of them or replace and overwrite them. It's frequently not obvious when this is happening.This leads to some unfortunate situations. For example, see get.go in CL 228383. If
modload.ListModulesis called beforemodload.WriteGoMod, thego.modfile will contain spurious// indirectcomments. This happens becauseWriteGoModusesloaded.directto decide whether to mark a requirement as indirect, butListModulescallsLoadBuildList, which replacesloaded, so it appears that nothing is directly required.I think these side effects should be more obvious to callers, and the
loadedandbuildListvariables should be removed. Instead, theloaderstruct type should be exported, and exported functions that create, read, or write it should be converted to methods.