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