1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package cache
6
7import (
8	"context"
9	"fmt"
10	"go/ast"
11	"go/types"
12	"reflect"
13	"sort"
14	"sync"
15
16	"golang.org/x/sync/errgroup"
17	"golang.org/x/tools/go/analysis"
18	"golang.org/x/tools/internal/analysisinternal"
19	"golang.org/x/tools/internal/event"
20	"golang.org/x/tools/internal/lsp/debug/tag"
21	"golang.org/x/tools/internal/lsp/source"
22	"golang.org/x/tools/internal/memoize"
23	"golang.org/x/tools/internal/span"
24	errors "golang.org/x/xerrors"
25)
26
27func (s *snapshot) Analyze(ctx context.Context, id string, analyzers []*source.Analyzer) ([]*source.Diagnostic, error) {
28	var roots []*actionHandle
29
30	for _, a := range analyzers {
31
32		if !a.IsEnabled(s.view) {
33			continue
34		}
35		ah, err := s.actionHandle(ctx, packageID(id), a.Analyzer)
36		if err != nil {
37			return nil, err
38		}
39		roots = append(roots, ah)
40	}
41
42	// Check if the context has been canceled before running the analyses.
43	if ctx.Err() != nil {
44		return nil, ctx.Err()
45	}
46
47	var results []*source.Diagnostic
48	for _, ah := range roots {
49		diagnostics, _, err := ah.analyze(ctx, s)
50		if err != nil {
51			return nil, err
52		}
53		results = append(results, diagnostics...)
54	}
55	return results, nil
56}
57
58type actionHandleKey string
59
60// An action represents one unit of analysis work: the application of
61// one analysis to one package. Actions form a DAG, both within a
62// package (as different analyzers are applied, either in sequence or
63// parallel), and across packages (as dependencies are analyzed).
64type actionHandle struct {
65	handle *memoize.Handle
66
67	analyzer *analysis.Analyzer
68	pkg      *pkg
69}
70
71type actionData struct {
72	diagnostics  []*source.Diagnostic
73	result       interface{}
74	objectFacts  map[objectFactKey]analysis.Fact
75	packageFacts map[packageFactKey]analysis.Fact
76	err          error
77}
78
79type objectFactKey struct {
80	obj types.Object
81	typ reflect.Type
82}
83
84type packageFactKey struct {
85	pkg *types.Package
86	typ reflect.Type
87}
88
89func (s *snapshot) actionHandle(ctx context.Context, id packageID, a *analysis.Analyzer) (*actionHandle, error) {
90	ph, err := s.buildPackageHandle(ctx, id, source.ParseFull)
91	if err != nil {
92		return nil, err
93	}
94	act := s.getActionHandle(id, ph.mode, a)
95	if act != nil {
96		return act, nil
97	}
98	if len(ph.key) == 0 {
99		return nil, errors.Errorf("actionHandle: no key for package %s", id)
100	}
101	pkg, err := ph.check(ctx, s)
102	if err != nil {
103		return nil, err
104	}
105	act = &actionHandle{
106		analyzer: a,
107		pkg:      pkg,
108	}
109	var deps []*actionHandle
110	// Add a dependency on each required analyzers.
111	for _, req := range a.Requires {
112		reqActionHandle, err := s.actionHandle(ctx, id, req)
113		if err != nil {
114			return nil, err
115		}
116		deps = append(deps, reqActionHandle)
117	}
118
119	// TODO(golang/go#35089): Re-enable this when we doesn't use ParseExported
120	// mode for dependencies. In the meantime, disable analysis for dependencies,
121	// since we don't get anything useful out of it.
122	if false {
123		// An analysis that consumes/produces facts
124		// must run on the package's dependencies too.
125		if len(a.FactTypes) > 0 {
126			importIDs := make([]string, 0, len(ph.m.deps))
127			for _, importID := range ph.m.deps {
128				importIDs = append(importIDs, string(importID))
129			}
130			sort.Strings(importIDs) // for determinism
131			for _, importID := range importIDs {
132				depActionHandle, err := s.actionHandle(ctx, packageID(importID), a)
133				if err != nil {
134					return nil, err
135				}
136				deps = append(deps, depActionHandle)
137			}
138		}
139	}
140
141	h := s.generation.Bind(buildActionKey(a, ph), func(ctx context.Context, arg memoize.Arg) interface{} {
142		snapshot := arg.(*snapshot)
143		// Analyze dependencies first.
144		results, err := execAll(ctx, snapshot, deps)
145		if err != nil {
146			return &actionData{
147				err: err,
148			}
149		}
150		return runAnalysis(ctx, snapshot, a, pkg, results)
151	}, nil)
152	act.handle = h
153
154	act = s.addActionHandle(act)
155	return act, nil
156}
157
158func (act *actionHandle) analyze(ctx context.Context, snapshot *snapshot) ([]*source.Diagnostic, interface{}, error) {
159	d, err := act.handle.Get(ctx, snapshot.generation, snapshot)
160	if err != nil {
161		return nil, nil, err
162	}
163	data, ok := d.(*actionData)
164	if !ok {
165		return nil, nil, errors.Errorf("unexpected type for %s:%s", act.pkg.ID(), act.analyzer.Name)
166	}
167	if data == nil {
168		return nil, nil, errors.Errorf("unexpected nil analysis for %s:%s", act.pkg.ID(), act.analyzer.Name)
169	}
170	return data.diagnostics, data.result, data.err
171}
172
173func buildActionKey(a *analysis.Analyzer, ph *packageHandle) actionHandleKey {
174	return actionHandleKey(hashContents([]byte(fmt.Sprintf("%p %s", a, string(ph.key)))))
175}
176
177func (act *actionHandle) String() string {
178	return fmt.Sprintf("%s@%s", act.analyzer, act.pkg.PkgPath())
179}
180
181func execAll(ctx context.Context, snapshot *snapshot, actions []*actionHandle) (map[*actionHandle]*actionData, error) {
182	var mu sync.Mutex
183	results := make(map[*actionHandle]*actionData)
184
185	g, ctx := errgroup.WithContext(ctx)
186	for _, act := range actions {
187		act := act
188		g.Go(func() error {
189			v, err := act.handle.Get(ctx, snapshot.generation, snapshot)
190			if err != nil {
191				return err
192			}
193			data, ok := v.(*actionData)
194			if !ok {
195				return errors.Errorf("unexpected type for %s: %T", act, v)
196			}
197
198			mu.Lock()
199			defer mu.Unlock()
200			results[act] = data
201
202			return nil
203		})
204	}
205	return results, g.Wait()
206}
207
208func runAnalysis(ctx context.Context, snapshot *snapshot, analyzer *analysis.Analyzer, pkg *pkg, deps map[*actionHandle]*actionData) (data *actionData) {
209	data = &actionData{
210		objectFacts:  make(map[objectFactKey]analysis.Fact),
211		packageFacts: make(map[packageFactKey]analysis.Fact),
212	}
213	defer func() {
214		if r := recover(); r != nil {
215			data.err = errors.Errorf("analysis %s for package %s panicked: %v", analyzer.Name, pkg.PkgPath(), r)
216		}
217	}()
218
219	// Plumb the output values of the dependencies
220	// into the inputs of this action.  Also facts.
221	inputs := make(map[*analysis.Analyzer]interface{})
222
223	for depHandle, depData := range deps {
224		if depHandle.pkg == pkg {
225			// Same package, different analysis (horizontal edge):
226			// in-memory outputs of prerequisite analyzers
227			// become inputs to this analysis pass.
228			inputs[depHandle.analyzer] = depData.result
229		} else if depHandle.analyzer == analyzer { // (always true)
230			// Same analysis, different package (vertical edge):
231			// serialized facts produced by prerequisite analysis
232			// become available to this analysis pass.
233			for key, fact := range depData.objectFacts {
234				// Filter out facts related to objects
235				// that are irrelevant downstream
236				// (equivalently: not in the compiler export data).
237				if !exportedFrom(key.obj, depHandle.pkg.types) {
238					continue
239				}
240				data.objectFacts[key] = fact
241			}
242			for key, fact := range depData.packageFacts {
243				// TODO: filter out facts that belong to
244				// packages not mentioned in the export data
245				// to prevent side channels.
246
247				data.packageFacts[key] = fact
248			}
249		}
250	}
251
252	var syntax []*ast.File
253	for _, cgf := range pkg.compiledGoFiles {
254		syntax = append(syntax, cgf.File)
255	}
256
257	var diagnostics []*analysis.Diagnostic
258
259	// Run the analysis.
260	pass := &analysis.Pass{
261		Analyzer:   analyzer,
262		Fset:       snapshot.view.session.cache.fset,
263		Files:      syntax,
264		Pkg:        pkg.GetTypes(),
265		TypesInfo:  pkg.GetTypesInfo(),
266		TypesSizes: pkg.GetTypesSizes(),
267		ResultOf:   inputs,
268		Report: func(d analysis.Diagnostic) {
269			// Prefix the diagnostic category with the analyzer's name.
270			if d.Category == "" {
271				d.Category = analyzer.Name
272			} else {
273				d.Category = analyzer.Name + "." + d.Category
274			}
275			diagnostics = append(diagnostics, &d)
276		},
277		ImportObjectFact: func(obj types.Object, ptr analysis.Fact) bool {
278			if obj == nil {
279				panic("nil object")
280			}
281			key := objectFactKey{obj, factType(ptr)}
282
283			if v, ok := data.objectFacts[key]; ok {
284				reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
285				return true
286			}
287			return false
288		},
289		ExportObjectFact: func(obj types.Object, fact analysis.Fact) {
290			if obj.Pkg() != pkg.types {
291				panic(fmt.Sprintf("internal error: in analysis %s of package %s: Fact.Set(%s, %T): can't set facts on objects belonging another package",
292					analyzer, pkg.ID(), obj, fact))
293			}
294			key := objectFactKey{obj, factType(fact)}
295			data.objectFacts[key] = fact // clobber any existing entry
296		},
297		ImportPackageFact: func(pkg *types.Package, ptr analysis.Fact) bool {
298			if pkg == nil {
299				panic("nil package")
300			}
301			key := packageFactKey{pkg, factType(ptr)}
302			if v, ok := data.packageFacts[key]; ok {
303				reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
304				return true
305			}
306			return false
307		},
308		ExportPackageFact: func(fact analysis.Fact) {
309			key := packageFactKey{pkg.types, factType(fact)}
310			data.packageFacts[key] = fact // clobber any existing entry
311		},
312		AllObjectFacts: func() []analysis.ObjectFact {
313			facts := make([]analysis.ObjectFact, 0, len(data.objectFacts))
314			for k := range data.objectFacts {
315				facts = append(facts, analysis.ObjectFact{Object: k.obj, Fact: data.objectFacts[k]})
316			}
317			return facts
318		},
319		AllPackageFacts: func() []analysis.PackageFact {
320			facts := make([]analysis.PackageFact, 0, len(data.packageFacts))
321			for k := range data.packageFacts {
322				facts = append(facts, analysis.PackageFact{Package: k.pkg, Fact: data.packageFacts[k]})
323			}
324			return facts
325		},
326	}
327	analysisinternal.SetTypeErrors(pass, pkg.typeErrors)
328
329	if pkg.IsIllTyped() {
330		data.err = errors.Errorf("analysis skipped due to errors in package")
331		return data
332	}
333	data.result, data.err = pass.Analyzer.Run(pass)
334	if data.err != nil {
335		return data
336	}
337
338	if got, want := reflect.TypeOf(data.result), pass.Analyzer.ResultType; got != want {
339		data.err = errors.Errorf(
340			"internal error: on package %s, analyzer %s returned a result of type %v, but declared ResultType %v",
341			pass.Pkg.Path(), pass.Analyzer, got, want)
342		return data
343	}
344
345	// disallow calls after Run
346	pass.ExportObjectFact = func(obj types.Object, fact analysis.Fact) {
347		panic(fmt.Sprintf("%s:%s: Pass.ExportObjectFact(%s, %T) called after Run", analyzer.Name, pkg.PkgPath(), obj, fact))
348	}
349	pass.ExportPackageFact = func(fact analysis.Fact) {
350		panic(fmt.Sprintf("%s:%s: Pass.ExportPackageFact(%T) called after Run", analyzer.Name, pkg.PkgPath(), fact))
351	}
352
353	for _, diag := range diagnostics {
354		srcDiags, err := analysisDiagnosticDiagnostics(snapshot, pkg, analyzer, diag)
355		if err != nil {
356			event.Error(ctx, "unable to compute analysis error position", err, tag.Category.Of(diag.Category), tag.Package.Of(pkg.ID()))
357			continue
358		}
359		if ctx.Err() != nil {
360			data.err = ctx.Err()
361			return data
362		}
363		data.diagnostics = append(data.diagnostics, srcDiags...)
364	}
365	return data
366}
367
368// exportedFrom reports whether obj may be visible to a package that imports pkg.
369// This includes not just the exported members of pkg, but also unexported
370// constants, types, fields, and methods, perhaps belonging to oether packages,
371// that find there way into the API.
372// This is an overapproximation of the more accurate approach used by
373// gc export data, which walks the type graph, but it's much simpler.
374//
375// TODO(adonovan): do more accurate filtering by walking the type graph.
376func exportedFrom(obj types.Object, pkg *types.Package) bool {
377	switch obj := obj.(type) {
378	case *types.Func:
379		return obj.Exported() && obj.Pkg() == pkg ||
380			obj.Type().(*types.Signature).Recv() != nil
381	case *types.Var:
382		return obj.Exported() && obj.Pkg() == pkg ||
383			obj.IsField()
384	case *types.TypeName, *types.Const:
385		return true
386	}
387	return false // Nil, Builtin, Label, or PkgName
388}
389
390func factType(fact analysis.Fact) reflect.Type {
391	t := reflect.TypeOf(fact)
392	if t.Kind() != reflect.Ptr {
393		panic(fmt.Sprintf("invalid Fact type: got %T, want pointer", t))
394	}
395	return t
396}
397
398func (s *snapshot) DiagnosePackage(ctx context.Context, spkg source.Package) (map[span.URI][]*source.Diagnostic, error) {
399	pkg := spkg.(*pkg)
400	// Apply type error analyzers. They augment type error diagnostics with their own fixes.
401	var analyzers []*source.Analyzer
402	for _, a := range s.View().Options().TypeErrorAnalyzers {
403		analyzers = append(analyzers, a)
404	}
405	var errorAnalyzerDiag []*source.Diagnostic
406	if pkg.HasTypeErrors() {
407		var err error
408		errorAnalyzerDiag, err = s.Analyze(ctx, pkg.ID(), analyzers)
409		if err != nil {
410			// Keep going: analysis failures should not block diagnostics.
411			event.Error(ctx, "type error analysis failed", err, tag.Package.Of(pkg.ID()))
412		}
413	}
414	diags := map[span.URI][]*source.Diagnostic{}
415	for _, diag := range pkg.diagnostics {
416		for _, eaDiag := range errorAnalyzerDiag {
417			if eaDiag.URI == diag.URI && eaDiag.Range == diag.Range && eaDiag.Message == diag.Message {
418				// Type error analyzers just add fixes and tags. Make a copy,
419				// since we don't own either, and overwrite.
420				// The analyzer itself can't do this merge because
421				// analysis.Diagnostic doesn't have all the fields, and Analyze
422				// can't because it doesn't have the type error, notably its code.
423				clone := *diag
424				clone.SuggestedFixes = eaDiag.SuggestedFixes
425				clone.Tags = eaDiag.Tags
426				clone.Analyzer = eaDiag.Analyzer
427				diag = &clone
428			}
429		}
430		diags[diag.URI] = append(diags[diag.URI], diag)
431	}
432	return diags, nil
433}
434