@@ -17,8 +17,6 @@ package ir
17
17
import (
18
18
"slices"
19
19
20
- "github.com/bits-and-blooms/bitset"
21
-
22
20
"github.com/bufbuild/protocompile/experimental/ast"
23
21
"github.com/bufbuild/protocompile/experimental/seq"
24
22
"github.com/bufbuild/protocompile/internal/ext/mapsx"
@@ -32,6 +30,7 @@ type Import struct {
32
30
Public , Weak bool // The kind of import this is.
33
31
Direct bool // Whether this is a direct or transitive import.
34
32
Visible bool // Whether this import's symbols are visible in the current file.
33
+ Used bool // Whether this import has been marked as used.
35
34
Decl ast.DeclImport // The import declaration.
36
35
}
37
36
@@ -57,21 +56,22 @@ type imports struct {
57
56
// 2. Weak imports.
58
57
// 3. Regular imports.
59
58
// 4. Transitive public imports.
60
- // 4 . Transitive imports.
59
+ // 5 . Transitive imports.
61
60
//
62
61
// The fields after this one specify where each of these segments ends.
63
62
//
64
63
// The last element of this slice is always descriptor.proto, even if it
65
64
// exists elsewhere as an ordinary import.
66
65
files []imported
67
66
68
- // Which of the above files we are permitted to import from.
69
- visible bitset.BitSet
70
-
71
67
// Maps the path of each file to its index in files. This is used for
72
68
// mapping from one [Context]'s file IDs to another's.
73
69
byPath intern.Map [uint32 ]
74
70
71
+ // Map of path of each imported file to a direct import which causes it to
72
+ // be imported. This is used for marking which imports are used.
73
+ causes intern.Map [uint32 ]
74
+
75
75
// NOTE: public imports always come first. This ensures that when
76
76
// recursively determining public imports, we consider public imports'
77
77
// recursive imports first. Consider the following sequence of files:
@@ -105,8 +105,9 @@ type imports struct {
105
105
106
106
// imported wraps an imported [File] and the import statement declaration [ast.DeclImport].
107
107
type imported struct {
108
- file File
109
- decl ast.DeclImport
108
+ file File
109
+ decl ast.DeclImport
110
+ visible , used bool
110
111
}
111
112
112
113
// Append appends a direct import to this imports table.
@@ -132,7 +133,7 @@ func (i *imports) AddDirect(imp Import) {
132
133
//
133
134
// Must only be called once, after all direct imports are added.
134
135
func (i * imports ) Recurse (dedup intern.Map [ast.DeclImport ]) {
135
- for file := range seq .Values (i .Directs ()) {
136
+ for _ , file := range seq .All (i .Directs ()) {
136
137
for imp := range seq .Values (file .TransitiveImports ()) {
137
138
if ! mapsx .AddZero (dedup , imp .InternedPath ()) {
138
139
continue
@@ -150,6 +151,20 @@ func (i *imports) Recurse(dedup intern.Map[ast.DeclImport]) {
150
151
i .Insert (imp , - 1 , imp .Public )
151
152
}
152
153
}
154
+
155
+ // Now, build the path and causes maps.
156
+ i .byPath = make (intern.Map [uint32 ])
157
+ i .causes = make (intern.Map [uint32 ])
158
+
159
+ for n , imp := range i .files {
160
+ i .byPath [imp .file .InternedPath ()] = uint32 (n )
161
+ }
162
+ for k , file := range seq .All (i .Directs ()) {
163
+ mapsx .Add (i .causes , file .InternedPath (), uint32 (k ))
164
+ for imp := range seq .Values (file .TransitiveImports ()) {
165
+ mapsx .Add (i .causes , imp .InternedPath (), uint32 (k ))
166
+ }
167
+ }
153
168
}
154
169
155
170
// Insert inserts a new import at the given position.
@@ -160,13 +175,19 @@ func (i *imports) Insert(imp Import, pos int, visible bool) {
160
175
pos = len (i .files )
161
176
}
162
177
163
- if i .byPath == nil {
164
- i .byPath = make (intern.Map [uint32 ])
165
- }
178
+ i .files = slices .Insert (i .files , pos , imported {
179
+ file : imp .File ,
180
+ decl : imp .Decl ,
181
+ visible : visible ,
182
+ })
183
+ }
166
184
167
- i .files = slices .Insert (i .files , pos , imported {file : imp .File , decl : imp .Decl })
168
- i .byPath [imp .File .InternedPath ()] = uint32 (pos )
169
- i .visible .SetTo (uint (pos ), visible )
185
+ // MarkUsed records a file as used, which affects the values of [Import].Used.
186
+ func (i * imports ) MarkUsed (file File ) {
187
+ idx , ok := i .causes [file .InternedPath ()]
188
+ if ok {
189
+ i .files [idx ].used = true
190
+ }
170
191
}
171
192
172
193
// DescriptorProto returns the file for descriptor.proto.
@@ -181,21 +202,25 @@ func (i *imports) Directs() seq.Indexer[Import] {
181
202
i .files [:i .importEnd ],
182
203
func (j int , imported imported ) Import {
183
204
n := uint32 (j )
205
+ public := n < i .publicEnd
184
206
return Import {
185
207
File : imported .file ,
186
- Public : n < i . publicEnd ,
187
- Weak : n >= i . publicEnd && n < i .weakEnd ,
208
+ Public : public ,
209
+ Weak : ! public && n < i .weakEnd ,
188
210
Direct : true ,
189
211
Visible : true ,
190
212
Decl : imported .decl ,
213
+
214
+ // Public imports are implicitly always used.
215
+ Used : imported .used || public ,
191
216
}
192
217
},
193
218
)
194
219
}
195
220
196
221
// Transitive returns an indexer over the Transitive imports.
197
222
//
198
- // This function does not report whether those imports are weak or not .
223
+ // This function does not report whether those imports are weak or used .
199
224
func (i * imports ) Transitive () seq.Indexer [Import ] {
200
225
return seq .NewFixedSlice (
201
226
i .files [:max (0 , len (i .files )- 1 )], // Exclude the implicit descriptor.proto.
@@ -206,7 +231,7 @@ func (i *imports) Transitive() seq.Indexer[Import] {
206
231
Public : n < i .publicEnd ||
207
232
(n >= i .importEnd && n < i .transPublicEnd ),
208
233
Direct : n < i .importEnd ,
209
- Visible : i .visible . Test ( uint ( j )) ,
234
+ Visible : imported .visible ,
210
235
Decl : imported .decl ,
211
236
}
212
237
},
0 commit comments