github.com/v2fly/tools@v0.100.0/internal/lsp/cache/analysis.go (about) 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 5 package cache 6 7 import ( 8 "context" 9 "fmt" 10 "go/ast" 11 "go/types" 12 "reflect" 13 "sort" 14 "sync" 15 16 "github.com/v2fly/tools/go/analysis" 17 "github.com/v2fly/tools/internal/analysisinternal" 18 "github.com/v2fly/tools/internal/event" 19 "github.com/v2fly/tools/internal/lsp/debug/tag" 20 "github.com/v2fly/tools/internal/lsp/source" 21 "github.com/v2fly/tools/internal/memoize" 22 "github.com/v2fly/tools/internal/span" 23 "golang.org/x/sync/errgroup" 24 errors "golang.org/x/xerrors" 25 ) 26 27 func (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 58 type 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). 64 type actionHandle struct { 65 handle *memoize.Handle 66 67 analyzer *analysis.Analyzer 68 pkg *pkg 69 } 70 71 type 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 79 type objectFactKey struct { 80 obj types.Object 81 typ reflect.Type 82 } 83 84 type packageFactKey struct { 85 pkg *types.Package 86 typ reflect.Type 87 } 88 89 func (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 158 func (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 173 func buildActionKey(a *analysis.Analyzer, ph *packageHandle) actionHandleKey { 174 return actionHandleKey(hashContents([]byte(fmt.Sprintf("%p %s", a, string(ph.key))))) 175 } 176 177 func (act *actionHandle) String() string { 178 return fmt.Sprintf("%s@%s", act.analyzer, act.pkg.PkgPath()) 179 } 180 181 func 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 208 func 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. 376 func 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 390 func 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 398 func (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 }