@@ -53,16 +53,118 @@ func NewPackageLoader(log logutils.Log, cfg *config.Config, args []string, env *
5353
5454// Load loads packages. 
5555func  (l  * PackageLoader ) Load (ctx  context.Context , linters  []* linter.Config ) (pkgs , deduplicatedPkgs  []* packages.Package , err  error ) {
56+ 	// Check for multiple modules and provide helpful error 
57+ 	if  err  :=  l .detectMultipleModules (); err  !=  nil  {
58+ 		return  nil , nil , err 
59+ 	}
60+ 
5661	loadMode  :=  findLoadMode (linters )
5762
58- 	pkgs , err   =  l .loadPackages (ctx , loadMode )
59- 	if  err  !=  nil  {
60- 		return  nil , nil , fmt .Errorf ("failed to load packages: %w" , err )
63+ 	pkgs , loadErr   : =l .loadPackages (ctx , loadMode )
64+ 	if  loadErr  !=  nil  {
65+ 		return  nil , nil , fmt .Errorf ("failed to load packages: %w" , loadErr )
6166	}
6267
6368	return  pkgs , l .filterDuplicatePackages (pkgs ), nil 
6469}
6570
71+ // detectMultipleModules checks if multiple arguments refer to different modules 
72+ func  (l  * PackageLoader ) detectMultipleModules () error  {
73+ 	if  len (l .args ) <=  1  {
74+ 		return  nil 
75+ 	}
76+ 
77+ 	var  moduleRoots  []string 
78+ 	seenRoots  :=  make (map [string ]bool )
79+ 
80+ 	for  _ , arg  :=  range  l .args  {
81+ 		moduleRoot  :=  l .findModuleRootForArg (arg )
82+ 		if  moduleRoot  !=  ""  &&  ! seenRoots [moduleRoot ] {
83+ 			moduleRoots  =  append (moduleRoots , moduleRoot )
84+ 			seenRoots [moduleRoot ] =  true 
85+ 		}
86+ 	}
87+ 
88+ 	if  len (moduleRoots ) >  1  {
89+ 		return  fmt .Errorf ("multiple Go modules detected: %v\n \n " + 
90+ 			"Multi-module analysis is not supported. Each module should be analyzed separately:\n " + 
91+ 			"  golangci-lint run %s\n   golangci-lint run %s" ,
92+ 			moduleRoots , moduleRoots [0 ], moduleRoots [1 ])
93+ 	}
94+ 
95+ 	return  nil 
96+ }
97+ 
98+ // findModuleRootForArg finds the module root for a given argument using go env 
99+ func  (l  * PackageLoader ) findModuleRootForArg (arg  string ) string  {
100+ 	absPath , err  :=  filepath .Abs (arg )
101+ 	if  err  !=  nil  {
102+ 		if  l .debugf  !=  nil  {
103+ 			l .debugf ("Failed to get absolute path for %s: %v" , arg , err )
104+ 		}
105+ 		return  "" 
106+ 	}
107+ 
108+ 	// Determine the directory to check 
109+ 	var  targetDir  string 
110+ 	if  info , statErr  :=  os .Stat (absPath ); statErr  ==  nil  &&  info .IsDir () {
111+ 		targetDir  =  absPath 
112+ 	} else  if  statErr  ==  nil  {
113+ 		targetDir  =  filepath .Dir (absPath )
114+ 	} else  {
115+ 		return  "" 
116+ 	}
117+ 
118+ 	// Save current directory 
119+ 	originalWd , err  :=  os .Getwd ()
120+ 	if  err  !=  nil  {
121+ 		if  l .debugf  !=  nil  {
122+ 			l .debugf ("Failed to get current directory: %v" , err )
123+ 		}
124+ 		return  "" 
125+ 	}
126+ 	defer  func () {
127+ 		if  chErr  :=  os .Chdir (originalWd ); chErr  !=  nil  &&  l .debugf  !=  nil  {
128+ 			l .debugf ("Failed to restore directory %s: %v" , originalWd , chErr )
129+ 		}
130+ 	}()
131+ 
132+ 	// Change to target directory and use go env GOMOD 
133+ 	if  chdirErr  :=  os .Chdir (targetDir ); chdirErr  !=  nil  {
134+ 		if  l .debugf  !=  nil  {
135+ 			l .debugf ("Failed to change to directory %s: %v" , targetDir , chdirErr )
136+ 		}
137+ 		return  "" 
138+ 	}
139+ 
140+ 	goModPath , err  :=  goenv .GetOne (context .Background (), goenv .GOMOD )
141+ 	if  err  !=  nil  ||  goModPath  ==  ""  {
142+ 		if  l .debugf  !=  nil  {
143+ 			l .debugf ("go env GOMOD failed in %s: err=%v, path=%s" , targetDir , err , goModPath )
144+ 		}
145+ 		return  "" 
146+ 	}
147+ 
148+ 	return  filepath .Dir (goModPath )
149+ }
150+ 
151+ // determineWorkingDir determines the working directory for package loading. 
152+ // If the first argument is within a directory tree that has a go.mod file, use that module root. 
153+ // Otherwise, use the current working directory. 
154+ func  (l  * PackageLoader ) determineWorkingDir () string  {
155+ 	if  len (l .args ) ==  0  {
156+ 		return  "" 
157+ 	}
158+ 
159+ 	moduleRoot  :=  l .findModuleRootForArg (l .args [0 ])
160+ 	if  moduleRoot  !=  ""  {
161+ 		if  l .debugf  !=  nil  {
162+ 			l .debugf ("Found module root %s, using as working dir" , moduleRoot )
163+ 		}
164+ 	}
165+ 	return  moduleRoot 
166+ }
167+ 
66168func  (l  * PackageLoader ) loadPackages (ctx  context.Context , loadMode  packages.LoadMode ) ([]* packages.Package , error ) {
67169	defer  func (startedAt  time.Time ) {
68170		l .log .Infof ("Go packages loading at mode %s took %s" , stringifyLoadMode (loadMode ), time .Since (startedAt ))
@@ -76,10 +178,11 @@ func (l *PackageLoader) loadPackages(ctx context.Context, loadMode packages.Load
76178		Context :    ctx ,
77179		BuildFlags : l .makeBuildFlags (),
78180		Logf :       l .debugf ,
181+ 		Dir :        l .determineWorkingDir (),
79182		// TODO: use fset, parsefile, overlay 
80183	}
81184
82- 	args  :=  buildArgs ( l . args )
185+ 	args  :=  l . buildArgs ( )
83186
84187	l .debugf ("Built loader args are %s" , args )
85188
@@ -233,13 +336,23 @@ func (l *PackageLoader) makeBuildFlags() []string {
233336	return  buildFlags 
234337}
235338
236- func  buildArgs (args  []string ) []string  {
237- 	if  len (args ) ==  0  {
339+ // buildArgs processes the arguments for package loading, handling directory changes appropriately. 
340+ func  (l  * PackageLoader ) buildArgs () []string  {
341+ 	if  len (l .args ) ==  0  {
342+ 		return  []string {"./..." }
343+ 	}
344+ 
345+ 	workingDir  :=  l .determineWorkingDir ()
346+ 
347+ 	// If we're using a different working directory, we need to adjust the arguments 
348+ 	if  workingDir  !=  ""  {
349+ 		// We're switching to the target directory as working dir, so use "./..." to analyze it 
238350		return  []string {"./..." }
239351	}
240352
353+ 	// Use the original buildArgs logic for the current working directory 
241354	var  retArgs  []string 
242- 	for  _ , arg  :=  range  args  {
355+ 	for  _ , arg  :=  range  l . args  {
243356		if  strings .HasPrefix (arg , "." ) ||  filepath .IsAbs (arg ) {
244357			retArgs  =  append (retArgs , arg )
245358		} else  {
0 commit comments