|
| 1 | +// Copyright 2023 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 typerefs extracts from Go syntax a graph of symbol-level |
| 6 | +// dependencies, for the purpose of precise invalidation of package data. |
| 7 | +// |
| 8 | +// # Background |
| 9 | +// |
| 10 | +// The goal of this analysis is to determine, for each package P, a nearly |
| 11 | +// minimal set of packages that could affect the type checking of P. This set |
| 12 | +// may contain false positives, but the smaller this set the better we can |
| 13 | +// invalidate and prune packages in gopls. |
| 14 | +// |
| 15 | +// More precisely, for each package P we define the set of "reachable" packages |
| 16 | +// from P as the set of packages that may affect the (deep) export data of the |
| 17 | +// direct dependencies of P. By this definition, the complement of this set |
| 18 | +// cannot affect any information derived from type checking P (e.g. |
| 19 | +// diagnostics, cross references, or method sets). Therefore we need not |
| 20 | +// invalidate any results for P when a package in the complement of this set |
| 21 | +// changes. |
| 22 | +// |
| 23 | +// # Computing references |
| 24 | +// |
| 25 | +// For a given declaration D, references are computed based on identifiers or |
| 26 | +// dotted identifiers referenced in the declaration of D, that may affect |
| 27 | +// the type of D. However, these references reflect only local knowledge of the |
| 28 | +// package and its dependency metadata, and do not depend on any analysis of |
| 29 | +// the dependencies themselves. |
| 30 | +// |
| 31 | +// Specifically, if a referring identifier I appears in the declaration, we |
| 32 | +// record an edge from D to each object possibly referenced by I. We search for |
| 33 | +// references within type syntax, but do not actual type-check, so we can't |
| 34 | +// reliably determine whether an expression is a type or a term, or whether a |
| 35 | +// function is a builtin or generic. For example, the type of x in var x = |
| 36 | +// p.F(W) only depends on W if p.F is a builtin or generic function, which we |
| 37 | +// cannot know without type-checking package p. So we may over-approximate in |
| 38 | +// this way. |
| 39 | +// |
| 40 | +// - If I is declared in the current package, record a reference to its |
| 41 | +// declaration. |
| 42 | +// - Else, if there are any dot-imported imports in the current file and I is |
| 43 | +// exported, record a (possibly dangling) edge to the corresponding |
| 44 | +// declaration in each dot-imported package. |
| 45 | +// |
| 46 | +// If a dotted identifier q.I appears in the declaration, we |
| 47 | +// perform a similar operation: |
| 48 | +// - If q is declared in the current package, we record a reference to that |
| 49 | +// object. It may be a var or const that has a field or method I. |
| 50 | +// - Else, if q is a valid import name based on imports in the current file |
| 51 | +// and the provided metadata for dependency package names, record a |
| 52 | +// reference to the object I in that package. |
| 53 | +// - Additionally, handle the case where Q is exported, and Q.I may refer to |
| 54 | +// a field or method in a dot-imported package. |
| 55 | +// |
| 56 | +// That is essentially the entire algorithm, though there is some subtlety to |
| 57 | +// visiting the set of identifiers or dotted identifiers that may affect the |
| 58 | +// declaration type. See the visitDeclOrSpec function for the details of this |
| 59 | +// analysis. Notably, we also skip identifiers that refer to type parameters in |
| 60 | +// generic declarations. |
| 61 | +// |
| 62 | +// # API |
| 63 | +// |
| 64 | +// The main entry point for this analysis is the [Refs] function, which |
| 65 | +// implements the aforementioned syntactic analysis for a set of files |
| 66 | +// constituting a package. |
| 67 | +// |
| 68 | +// These references use shared state to efficiently represent references, by |
| 69 | +// way of the [PackageIndex] and [PackageSet] types. |
| 70 | +// |
| 71 | +// The [BuildPackageGraph] constructor implements a whole-graph analysis similar |
| 72 | +// to that which will be implemented by gopls, but for various reasons the |
| 73 | +// logic for this analysis will eventually live in the |
| 74 | +// [golang.org/x/tools/gopls/internal/lsp/cache] package. Nevertheless, |
| 75 | +// BuildPackageGraph and its test serve to verify the syntactic analysis, and |
| 76 | +// may serve as a proving ground for new optimizations of the whole-graph analysis. |
| 77 | +// |
| 78 | +// # Comparison with export data |
| 79 | +// |
| 80 | +// At first it may seem that the simplest way to implement this analysis would |
| 81 | +// be to consider the types.Packages of the dependencies of P, for example |
| 82 | +// during export. After all, it makes sense that the type checked packages |
| 83 | +// themselves could describe their dependencies. However, this does not work as |
| 84 | +// type information does not describe certain syntactic relationships. |
| 85 | +// |
| 86 | +// For example, the following scenarios cause type information to miss |
| 87 | +// syntactic relationships: |
| 88 | +// |
| 89 | +// Named type forwarding: |
| 90 | +// |
| 91 | +// package a; type A b.B |
| 92 | +// package b; type B int |
| 93 | +// |
| 94 | +// Aliases: |
| 95 | +// |
| 96 | +// package a; func A(f b.B) |
| 97 | +// package b; type B = func() |
| 98 | +// |
| 99 | +// Initializers: |
| 100 | +// |
| 101 | +// package a; var A = b.B() |
| 102 | +// package b; func B() string { return "hi" } |
| 103 | +// |
| 104 | +// Use of the unsafe package: |
| 105 | +// |
| 106 | +// package a; type A [unsafe.Sizeof(B{})]int |
| 107 | +// package b; type B struct { f1, f2, f3 int } |
| 108 | +// |
| 109 | +// In all of these examples, types do not contain information about the edge |
| 110 | +// between the a.A and b.B declarations. |
| 111 | +package typerefs |
0 commit comments