11package goanalysis
22
33import (
4- "errors"
54 "fmt"
6- "go/types"
7- "io"
8- "reflect"
95 "runtime/debug"
10- "time"
116
12- "golang.org/x/tools/go/analysis"
13- "golang.org/x/tools/go/packages"
14- "golang.org/x/tools/go/types/objectpath"
15-
16- "github.com/golangci/golangci-lint/internal/cache"
177 "github.com/golangci/golangci-lint/internal/errorutil"
18- "github.com/golangci/golangci-lint/pkg/goanalysis/pkgerrors"
198)
209
2110type actionAllocator struct {
@@ -39,54 +28,6 @@ func (actAlloc *actionAllocator) alloc() *action {
3928 return act
4029}
4130
42- // An action represents one unit of analysis work: the application of
43- // one analysis to one package. Actions form a DAG, both within a
44- // package (as different analyzers are applied, either in sequence or
45- // parallel), and across packages (as dependencies are analyzed).
46- type action struct {
47- a * analysis.Analyzer
48- pkg * packages.Package
49- pass * analysis.Pass
50- deps []* action
51- objectFacts map [objectFactKey ]analysis.Fact
52- packageFacts map [packageFactKey ]analysis.Fact
53- result any
54- diagnostics []analysis.Diagnostic
55- err error
56- r * runner
57- analysisDoneCh chan struct {}
58- loadCachedFactsDone bool
59- loadCachedFactsOk bool
60- isroot bool
61- isInitialPkg bool
62- needAnalyzeSource bool
63- }
64-
65- func (act * action ) String () string {
66- return fmt .Sprintf ("%s@%s" , act .a , act .pkg )
67- }
68-
69- func (act * action ) loadCachedFacts () bool {
70- if act .loadCachedFactsDone { // can't be set in parallel
71- return act .loadCachedFactsOk
72- }
73-
74- res := func () bool {
75- if act .isInitialPkg {
76- return true // load cached facts only for non-initial packages
77- }
78-
79- if len (act .a .FactTypes ) == 0 {
80- return true // no need to load facts
81- }
82-
83- return act .loadPersistedFacts ()
84- }()
85- act .loadCachedFactsDone = true
86- act .loadCachedFactsOk = res
87- return res
88- }
89-
9031func (act * action ) waitUntilDependingAnalyzersWorked () {
9132 for _ , dep := range act .deps {
9233 if dep .pkg == act .pkg {
@@ -113,265 +54,6 @@ func (act *action) analyzeSafe() {
11354 act .r .sw .TrackStage (act .a .Name , act .analyze )
11455}
11556
116- func (act * action ) analyze () {
117- defer close (act .analysisDoneCh ) // unblock actions depending on this action
118-
119- if ! act .needAnalyzeSource {
120- return
121- }
122-
123- defer func (now time.Time ) {
124- analyzeDebugf ("go/analysis: %s: %s: analyzed package %q in %s" , act .r .prefix , act .a .Name , act .pkg .Name , time .Since (now ))
125- }(time .Now ())
126-
127- // Report an error if any dependency failures.
128- var depErrors error
129- for _ , dep := range act .deps {
130- if dep .err == nil {
131- continue
132- }
133-
134- depErrors = errors .Join (depErrors , errors .Unwrap (dep .err ))
135- }
136- if depErrors != nil {
137- act .err = fmt .Errorf ("failed prerequisites: %w" , depErrors )
138- return
139- }
140-
141- // Plumb the output values of the dependencies
142- // into the inputs of this action. Also facts.
143- inputs := make (map [* analysis.Analyzer ]any )
144- startedAt := time .Now ()
145- for _ , dep := range act .deps {
146- if dep .pkg == act .pkg {
147- // Same package, different analysis (horizontal edge):
148- // in-memory outputs of prerequisite analyzers
149- // become inputs to this analysis pass.
150- inputs [dep .a ] = dep .result
151- } else if dep .a == act .a { // (always true)
152- // Same analysis, different package (vertical edge):
153- // serialized facts produced by prerequisite analysis
154- // become available to this analysis pass.
155- inheritFacts (act , dep )
156- }
157- }
158- factsDebugf ("%s: Inherited facts in %s" , act , time .Since (startedAt ))
159-
160- // Run the analysis.
161- pass := & analysis.Pass {
162- Analyzer : act .a ,
163- Fset : act .pkg .Fset ,
164- Files : act .pkg .Syntax ,
165- OtherFiles : act .pkg .OtherFiles ,
166- Pkg : act .pkg .Types ,
167- TypesInfo : act .pkg .TypesInfo ,
168- TypesSizes : act .pkg .TypesSizes ,
169- ResultOf : inputs ,
170- Report : func (d analysis.Diagnostic ) { act .diagnostics = append (act .diagnostics , d ) },
171- ImportObjectFact : act .importObjectFact ,
172- ExportObjectFact : act .exportObjectFact ,
173- ImportPackageFact : act .importPackageFact ,
174- ExportPackageFact : act .exportPackageFact ,
175- AllObjectFacts : act .allObjectFacts ,
176- AllPackageFacts : act .allPackageFacts ,
177- }
178- act .pass = pass
179- act .r .passToPkgGuard .Lock ()
180- act .r .passToPkg [pass ] = act .pkg
181- act .r .passToPkgGuard .Unlock ()
182-
183- if act .pkg .IllTyped {
184- // It looks like there should be !pass.Analyzer.RunDespiteErrors
185- // but govet's cgocall crashes on it. Govet itself contains !pass.Analyzer.RunDespiteErrors condition here,
186- // but it exits before it if packages.Load have failed.
187- act .err = fmt .Errorf ("analysis skipped: %w" , & pkgerrors.IllTypedError {Pkg : act .pkg })
188- } else {
189- startedAt = time .Now ()
190- act .result , act .err = pass .Analyzer .Run (pass )
191- analyzedIn := time .Since (startedAt )
192- if analyzedIn > time .Millisecond * 10 {
193- debugf ("%s: run analyzer in %s" , act , analyzedIn )
194- }
195- }
196-
197- // disallow calls after Run
198- pass .ExportObjectFact = nil
199- pass .ExportPackageFact = nil
200-
201- if err := act .persistFactsToCache (); err != nil {
202- act .r .log .Warnf ("Failed to persist facts to cache: %s" , err )
203- }
204- }
205-
206- // importObjectFact implements Pass.ImportObjectFact.
207- // Given a non-nil pointer ptr of type *T, where *T satisfies Fact,
208- // importObjectFact copies the fact value to *ptr.
209- func (act * action ) importObjectFact (obj types.Object , ptr analysis.Fact ) bool {
210- if obj == nil {
211- panic ("nil object" )
212- }
213- key := objectFactKey {obj , act .factType (ptr )}
214- if v , ok := act .objectFacts [key ]; ok {
215- reflect .ValueOf (ptr ).Elem ().Set (reflect .ValueOf (v ).Elem ())
216- return true
217- }
218- return false
219- }
220-
221- // exportObjectFact implements Pass.ExportObjectFact.
222- func (act * action ) exportObjectFact (obj types.Object , fact analysis.Fact ) {
223- if obj .Pkg () != act .pkg .Types {
224- act .r .log .Panicf ("internal error: in analysis %s of package %s: Fact.Set(%s, %T): can't set facts on objects belonging another package" ,
225- act .a , act .pkg , obj , fact )
226- }
227-
228- key := objectFactKey {obj , act .factType (fact )}
229- act .objectFacts [key ] = fact // clobber any existing entry
230- if isFactsExportDebug {
231- objstr := types .ObjectString (obj , (* types .Package ).Name )
232- factsExportDebugf ("%s: object %s has fact %s\n " ,
233- act .pkg .Fset .Position (obj .Pos ()), objstr , fact )
234- }
235- }
236-
237- func (act * action ) allObjectFacts () []analysis.ObjectFact {
238- out := make ([]analysis.ObjectFact , 0 , len (act .objectFacts ))
239- for key , fact := range act .objectFacts {
240- out = append (out , analysis.ObjectFact {
241- Object : key .obj ,
242- Fact : fact ,
243- })
244- }
245- return out
246- }
247-
248- // importPackageFact implements Pass.ImportPackageFact.
249- // Given a non-nil pointer ptr of type *T, where *T satisfies Fact,
250- // fact copies the fact value to *ptr.
251- func (act * action ) importPackageFact (pkg * types.Package , ptr analysis.Fact ) bool {
252- if pkg == nil {
253- panic ("nil package" )
254- }
255- key := packageFactKey {pkg , act .factType (ptr )}
256- if v , ok := act .packageFacts [key ]; ok {
257- reflect .ValueOf (ptr ).Elem ().Set (reflect .ValueOf (v ).Elem ())
258- return true
259- }
260- return false
261- }
262-
263- // exportPackageFact implements Pass.ExportPackageFact.
264- func (act * action ) exportPackageFact (fact analysis.Fact ) {
265- key := packageFactKey {act .pass .Pkg , act .factType (fact )}
266- act .packageFacts [key ] = fact // clobber any existing entry
267- factsDebugf ("%s: package %s has fact %s\n " ,
268- act .pkg .Fset .Position (act .pass .Files [0 ].Pos ()), act .pass .Pkg .Path (), fact )
269- }
270-
271- func (act * action ) allPackageFacts () []analysis.PackageFact {
272- out := make ([]analysis.PackageFact , 0 , len (act .packageFacts ))
273- for key , fact := range act .packageFacts {
274- out = append (out , analysis.PackageFact {
275- Package : key .pkg ,
276- Fact : fact ,
277- })
278- }
279- return out
280- }
281-
282- func (act * action ) factType (fact analysis.Fact ) reflect.Type {
283- t := reflect .TypeOf (fact )
284- if t .Kind () != reflect .Ptr {
285- act .r .log .Fatalf ("invalid Fact type: got %T, want pointer" , t )
286- }
287- return t
288- }
289-
290- func (act * action ) persistFactsToCache () error {
291- analyzer := act .a
292- if len (analyzer .FactTypes ) == 0 {
293- return nil
294- }
295-
296- // Merge new facts into the package and persist them.
297- var facts []Fact
298- for key , fact := range act .packageFacts {
299- if key .pkg != act .pkg .Types {
300- // The fact is from inherited facts from another package
301- continue
302- }
303- facts = append (facts , Fact {
304- Path : "" ,
305- Fact : fact ,
306- })
307- }
308- for key , fact := range act .objectFacts {
309- obj := key .obj
310- if obj .Pkg () != act .pkg .Types {
311- // The fact is from inherited facts from another package
312- continue
313- }
314-
315- path , err := objectpath .For (obj )
316- if err != nil {
317- // The object is not globally addressable
318- continue
319- }
320-
321- facts = append (facts , Fact {
322- Path : string (path ),
323- Fact : fact ,
324- })
325- }
326-
327- factsCacheDebugf ("Caching %d facts for package %q and analyzer %s" , len (facts ), act .pkg .Name , act .a .Name )
328-
329- key := fmt .Sprintf ("%s/facts" , analyzer .Name )
330- return act .r .pkgCache .Put (act .pkg , cache .HashModeNeedAllDeps , key , facts )
331- }
332-
333- func (act * action ) loadPersistedFacts () bool {
334- var facts []Fact
335- key := fmt .Sprintf ("%s/facts" , act .a .Name )
336- if err := act .r .pkgCache .Get (act .pkg , cache .HashModeNeedAllDeps , key , & facts ); err != nil {
337- if ! errors .Is (err , cache .ErrMissing ) && ! errors .Is (err , io .EOF ) {
338- act .r .log .Warnf ("Failed to get persisted facts: %s" , err )
339- }
340-
341- factsCacheDebugf ("No cached facts for package %q and analyzer %s" , act .pkg .Name , act .a .Name )
342- return false
343- }
344-
345- factsCacheDebugf ("Loaded %d cached facts for package %q and analyzer %s" , len (facts ), act .pkg .Name , act .a .Name )
346-
347- for _ , f := range facts {
348- if f .Path == "" { // this is a package fact
349- key := packageFactKey {act .pkg .Types , act .factType (f .Fact )}
350- act .packageFacts [key ] = f .Fact
351- continue
352- }
353- obj , err := objectpath .Object (act .pkg .Types , objectpath .Path (f .Path ))
354- if err != nil {
355- // Be lenient about these errors. For example, when
356- // analyzing io/ioutil from source, we may get a fact
357- // for methods on the devNull type, and objectpath
358- // will happily create a path for them. However, when
359- // we later load io/ioutil from export data, the path
360- // no longer resolves.
361- //
362- // If an exported type embeds the unexported type,
363- // then (part of) the unexported type will become part
364- // of the type information and our path will resolve
365- // again.
366- continue
367- }
368- factKey := objectFactKey {obj , act .factType (f .Fact )}
369- act .objectFacts [factKey ] = f .Fact
370- }
371-
372- return true
373- }
374-
37557func (act * action ) markDepsForAnalyzingSource () {
37658 // Horizontal deps (analyzer.Requires) must be loaded from source and analyzed before analyzing
37759 // this action.
0 commit comments