14
14
// permissions and limitations under the License.
15
15
16
16
using System ;
17
+ using System . Collections . Generic ;
18
+ using System . Diagnostics ;
19
+ using System . Linq ;
17
20
using System . Threading ;
18
21
using System . Threading . Tasks ;
22
+ using Microsoft . Python . Analysis . Core . DependencyResolution ;
19
23
using Microsoft . Python . Analysis . Modules ;
20
24
using Microsoft . Python . Analysis . Types ;
25
+ using Microsoft . Python . Core ;
21
26
using Microsoft . Python . Core . Collections ;
22
27
using Microsoft . Python . Parsing . Ast ;
23
28
24
29
namespace Microsoft . Python . Analysis . Analyzer {
30
+ [ DebuggerDisplay ( "{_module.Name}({_module.ModuleType})" ) ]
25
31
internal sealed class PythonAnalyzerEntry {
26
32
private readonly object _syncObj = new object ( ) ;
27
33
private TaskCompletionSource < IDocumentAnalysis > _analysisTcs ;
28
34
private IPythonModule _module ;
35
+ private bool _isUserModule ;
29
36
private PythonAst _ast ;
30
37
private IDocumentAnalysis _previousAnalysis ;
31
- private ImmutableArray < IPythonModule > _analysisDependencies ;
38
+ private HashSet < AnalysisModuleKey > _parserDependencies ;
39
+ private HashSet < AnalysisModuleKey > _analysisDependencies ;
32
40
private int _bufferVersion ;
33
41
private int _analysisVersion ;
34
42
@@ -40,29 +48,23 @@ public IPythonModule Module {
40
48
}
41
49
}
42
50
43
- public IDocumentAnalysis PreviousAnalysis {
44
- get {
45
- lock ( _syncObj ) {
46
- return _previousAnalysis ;
47
- }
48
- }
49
- }
50
-
51
- public ImmutableArray < IPythonModule > AnalysisDependencies {
51
+ public bool IsUserModule {
52
52
get {
53
53
lock ( _syncObj ) {
54
- return _analysisDependencies ;
54
+ return _isUserModule ;
55
55
}
56
56
}
57
57
}
58
58
59
- public int BufferVersion {
59
+ public IDocumentAnalysis PreviousAnalysis {
60
60
get {
61
61
lock ( _syncObj ) {
62
- return _bufferVersion ;
62
+ return _previousAnalysis ;
63
63
}
64
64
}
65
65
}
66
+
67
+ public int BufferVersion => _bufferVersion ;
66
68
67
69
public int AnalysisVersion {
68
70
get {
@@ -73,15 +75,13 @@ public int AnalysisVersion {
73
75
}
74
76
75
77
public bool NotAnalyzed => PreviousAnalysis is EmptyAnalysis ;
78
+
79
+ public PythonAnalyzerEntry ( EmptyAnalysis emptyAnalysis ) {
80
+ _previousAnalysis = emptyAnalysis ;
81
+ _isUserModule = emptyAnalysis . Document . ModuleType == ModuleType . User ;
76
82
77
- public PythonAnalyzerEntry ( IPythonModule module , PythonAst ast , IDocumentAnalysis previousAnalysis , int bufferVersion , int analysisVersion ) {
78
- _module = module ;
79
- _ast = ast ;
80
- _previousAnalysis = previousAnalysis ;
81
- _analysisDependencies = ImmutableArray < IPythonModule > . Empty ;
82
-
83
- _bufferVersion = bufferVersion ;
84
- _analysisVersion = analysisVersion ;
83
+ _bufferVersion = - 1 ;
84
+ _analysisVersion = 0 ;
85
85
_analysisTcs = new TaskCompletionSource < IDocumentAnalysis > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
86
86
}
87
87
@@ -92,7 +92,7 @@ public bool IsValidVersion(int version, out IPythonModule module, out PythonAst
92
92
lock ( _syncObj ) {
93
93
module = _module ;
94
94
ast = _ast ;
95
- return _previousAnalysis is EmptyAnalysis || _analysisVersion <= version ;
95
+ return _previousAnalysis is EmptyAnalysis || _isUserModule || _analysisVersion <= version ;
96
96
}
97
97
}
98
98
@@ -106,7 +106,7 @@ public void TrySetAnalysis(IDocumentAnalysis analysis, int version) {
106
106
return ;
107
107
}
108
108
109
- _analysisDependencies = ImmutableArray < IPythonModule > . Empty ;
109
+ _analysisDependencies = null ;
110
110
UpdateAnalysisTcs ( version ) ;
111
111
}
112
112
@@ -147,36 +147,142 @@ public void Invalidate(int analysisVersion) {
147
147
}
148
148
}
149
149
150
- public void Invalidate ( ImmutableArray < IPythonModule > dependencies , int analysisVersion ) {
150
+ public bool Invalidate ( ImmutableArray < IPythonModule > analysisDependencies , int analysisVersion , out ImmutableArray < AnalysisModuleKey > dependencies ) {
151
+ dependencies = ImmutableArray < AnalysisModuleKey > . Empty ;
152
+ IPythonModule module ;
153
+ int version ;
151
154
lock ( _syncObj ) {
152
155
if ( _analysisVersion >= analysisVersion ) {
153
- return ;
156
+ return false ;
157
+ }
158
+
159
+ version = _analysisVersion ;
160
+ module = _module ;
161
+ }
162
+
163
+ var dependenciesHashSet = new HashSet < AnalysisModuleKey > ( ) ;
164
+ foreach ( var dependency in analysisDependencies ) {
165
+ if ( dependency != module && ( dependency . ModuleType == ModuleType . User && dependency . Analysis . Version < version || dependency . Analysis is EmptyAnalysis ) ) {
166
+ dependenciesHashSet . Add ( new AnalysisModuleKey ( dependency ) ) ;
167
+ }
168
+ }
169
+
170
+ if ( dependenciesHashSet . Count == 0 ) {
171
+ return false ;
172
+ }
173
+
174
+ lock ( _syncObj ) {
175
+ if ( _analysisVersion >= analysisVersion ) {
176
+ return false ;
154
177
}
155
178
156
179
UpdateAnalysisTcs ( analysisVersion ) ;
157
- _analysisDependencies = _analysisDependencies . AddRange ( dependencies ) ;
180
+ if ( _analysisDependencies == null ) {
181
+ _analysisDependencies = dependenciesHashSet ;
182
+ } else {
183
+ var countBefore = _analysisDependencies . Count ;
184
+ _analysisDependencies . UnionWith ( dependenciesHashSet ) ;
185
+ if ( countBefore == _analysisDependencies . Count ) {
186
+ return false ;
187
+ }
188
+ }
189
+
190
+ dependencies = _parserDependencies != null
191
+ ? ImmutableArray < AnalysisModuleKey > . Create ( _parserDependencies . Union ( _analysisDependencies ) . ToArray ( ) )
192
+ : ImmutableArray < AnalysisModuleKey > . Create ( _analysisDependencies ) ;
193
+ return true ;
158
194
}
159
195
}
160
196
161
- public void Invalidate ( IPythonModule module , PythonAst ast , int bufferVersion , int analysisVersion ) {
197
+ public bool Invalidate ( IPythonModule module , PythonAst ast , int bufferVersion , int analysisVersion , out ImmutableArray < AnalysisModuleKey > dependencies ) {
198
+ dependencies = ImmutableArray < AnalysisModuleKey > . Empty ;
199
+ if ( _bufferVersion >= bufferVersion ) {
200
+ return false ;
201
+ }
202
+
203
+ var dependenciesHashSet = FindDependencies ( module , ast , bufferVersion ) ;
162
204
lock ( _syncObj ) {
163
205
if ( _analysisVersion >= analysisVersion && _bufferVersion >= bufferVersion ) {
164
- return ;
206
+ return false ;
165
207
}
166
208
167
209
_ast = ast ;
168
210
_module = module ;
169
- _bufferVersion = bufferVersion ;
211
+ _isUserModule = module . ModuleType == ModuleType . User ;
212
+ _parserDependencies = dependenciesHashSet ;
170
213
214
+ Interlocked . Exchange ( ref _bufferVersion , bufferVersion ) ;
171
215
UpdateAnalysisTcs ( analysisVersion ) ;
216
+ dependencies = _analysisDependencies != null
217
+ ? ImmutableArray < AnalysisModuleKey > . Create ( _parserDependencies . Union ( _analysisDependencies ) . ToArray ( ) )
218
+ : ImmutableArray < AnalysisModuleKey > . Create ( _parserDependencies ) ;
219
+ return true ;
220
+ }
221
+ }
222
+
223
+ private HashSet < AnalysisModuleKey > FindDependencies ( IPythonModule module , PythonAst ast , int bufferVersion ) {
224
+ var isTypeshed = module is StubPythonModule stub && stub . IsTypeshed ;
225
+ var moduleResolution = module . Interpreter . ModuleResolution ;
226
+ var pathResolver = isTypeshed
227
+ ? module . Interpreter . TypeshedResolution . CurrentPathResolver
228
+ : moduleResolution . CurrentPathResolver ;
229
+
230
+ var dependencies = new HashSet < AnalysisModuleKey > ( ) ;
231
+
232
+ if ( module . Stub != null ) {
233
+ dependencies . Add ( new AnalysisModuleKey ( module . Stub ) ) ;
234
+ }
235
+
236
+ foreach ( var node in ast . TraverseDepthFirst < Node > ( n => n . GetChildNodes ( ) ) ) {
237
+ if ( _bufferVersion > bufferVersion ) {
238
+ return dependencies ;
239
+ }
240
+
241
+ switch ( node ) {
242
+ case ImportStatement import :
243
+ foreach ( var moduleName in import . Names ) {
244
+ HandleSearchResults ( isTypeshed , dependencies , moduleResolution , pathResolver . FindImports ( module . FilePath , moduleName , import . ForceAbsolute ) ) ;
245
+ }
246
+ break ;
247
+ case FromImportStatement fromImport :
248
+ var imports = pathResolver . FindImports ( module . FilePath , fromImport ) ;
249
+ HandleSearchResults ( isTypeshed , dependencies , moduleResolution , imports ) ;
250
+ if ( imports is IImportChildrenSource childrenSource ) {
251
+ foreach ( var name in fromImport . Names ) {
252
+ if ( childrenSource . TryGetChildImport ( name . Name , out var childImport ) ) {
253
+ HandleSearchResults ( isTypeshed , dependencies , moduleResolution , childImport ) ;
254
+ }
255
+ }
256
+ }
257
+ break ;
258
+ }
259
+ }
260
+
261
+ dependencies . Remove ( new AnalysisModuleKey ( module ) ) ;
262
+ return dependencies ;
263
+ }
264
+
265
+ private static void HandleSearchResults ( bool isTypeshed , HashSet < AnalysisModuleKey > dependencies , IModuleManagement moduleResolution , IImportSearchResult searchResult ) {
266
+ switch ( searchResult ) {
267
+ case ModuleImport moduleImport when ! Ignore ( moduleResolution , moduleImport . FullName ) :
268
+ dependencies . Add ( new AnalysisModuleKey ( moduleImport . FullName , moduleImport . ModulePath , isTypeshed ) ) ;
269
+ return ;
270
+ case PossibleModuleImport possibleModuleImport when ! Ignore ( moduleResolution , possibleModuleImport . PrecedingModuleFullName ) :
271
+ dependencies . Add ( new AnalysisModuleKey ( possibleModuleImport . PrecedingModuleFullName , possibleModuleImport . PrecedingModulePath , isTypeshed ) ) ;
272
+ return ;
273
+ default :
274
+ return ;
172
275
}
173
276
}
174
277
278
+ private static bool Ignore ( IModuleManagement moduleResolution , string name )
279
+ => moduleResolution . BuiltinModuleName . EqualsOrdinal ( name ) || moduleResolution . GetSpecializedModule ( name ) != null ;
280
+
175
281
private void UpdateAnalysisTcs ( int analysisVersion ) {
176
282
_analysisVersion = analysisVersion ;
177
283
if ( _analysisTcs . Task . Status == TaskStatus . RanToCompletion ) {
178
284
_previousAnalysis = _analysisTcs . Task . Result ;
179
- _analysisDependencies = ImmutableArray < IPythonModule > . Empty ;
285
+ _analysisDependencies = null ;
180
286
}
181
287
182
288
if ( _analysisTcs . Task . IsCompleted ) {
0 commit comments