18
18
#include " swift/AST/Module.h"
19
19
#include " swift/AST/PrettyStackTrace.h"
20
20
#include " swift/AST/SourceFile.h"
21
+ #include " swift/Serialization/SerializedModuleLoader.h"
22
+ #include " swift/ClangImporter/ClangModule.h"
21
23
#include " swift/Basic/LangOptions.h"
22
24
#include " swift/Basic/PrettyStackTrace.h"
23
25
#include " swift/Basic/SourceManager.h"
28
30
#include " swift/Subsystems.h"
29
31
#include " llvm/ADT/Hashing.h"
30
32
#include " llvm/Support/MemoryBuffer.h"
33
+ #include " clang/AST/ASTContext.h"
31
34
32
35
using namespace swift ;
33
36
using namespace ide ;
@@ -162,10 +165,77 @@ static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC,
162
165
return newDC;
163
166
}
164
167
168
+ // / Check if any dependent files are modified since \p timestamp.
169
+ bool areAnyDependentFilesInvalidated (CompilerInstance &CI,
170
+ llvm::vfs::FileSystem &FS,
171
+ StringRef currentFileName,
172
+ llvm::sys::TimePoint<> timestamp) {
173
+
174
+ auto isInvalidated = [&](StringRef filePath) -> bool {
175
+ auto stat = FS.status (filePath);
176
+ if (!stat)
177
+ // Missing.
178
+ return true ;
179
+
180
+ auto lastModTime = stat->getLastModificationTime ();
181
+ if (lastModTime > timestamp)
182
+ // Modified.
183
+ return true ;
184
+
185
+ // If the last modification time is zero, this file is probably from a
186
+ // virtual file system. We need to check the content.
187
+ if (lastModTime == llvm::sys::TimePoint<>()) {
188
+ if (&CI.getFileSystem () == &FS)
189
+ return false ;
190
+
191
+ auto oldContent = CI.getFileSystem ().getBufferForFile (filePath);
192
+ auto newContent = FS.getBufferForFile (filePath);
193
+ if (!oldContent || !newContent)
194
+ // (unreachable?)
195
+ return true ;
196
+
197
+ if (oldContent.get ()->getBuffer () != newContent.get ()->getBuffer ())
198
+ // Different content.
199
+ return true ;
200
+ }
201
+
202
+ return false ;
203
+ };
204
+
205
+ // Check files in the current module.
206
+ for (FileUnit *file : CI.getMainModule ()->getFiles ()) {
207
+ StringRef filename;
208
+ if (auto SF = dyn_cast<SourceFile>(file))
209
+ filename = SF->getFilename ();
210
+ else if (auto LF = dyn_cast<LoadedFile>(file))
211
+ filename = LF->getFilename ();
212
+ else
213
+ continue ;
214
+
215
+ // Ignore the current file and synthesized files.
216
+ if (filename.empty () || filename.front () == ' <' ||
217
+ filename.equals (currentFileName))
218
+ continue ;
219
+
220
+ if (isInvalidated (filename))
221
+ return true ;
222
+ }
223
+
224
+ // Check other non-system depenencies (e.g. modules, headers).
225
+ for (auto &dep : CI.getDependencyTracker ()->getDependencies ()) {
226
+ if (isInvalidated (dep))
227
+ return true ;
228
+ }
229
+
230
+ // All loaded module files are not modified since the timestamp.
231
+ return false ;
232
+ }
233
+
165
234
} // namespace
166
235
167
236
bool CompletionInstance::performCachedOperationIfPossible (
168
237
const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash,
238
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
169
239
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
170
240
DiagnosticConsumer *DiagC,
171
241
llvm::function_ref<void (CompilerInstance &, bool )> Callback) {
@@ -187,10 +257,17 @@ bool CompletionInstance::performCachedOperationIfPossible(
187
257
auto &oldInfo = oldState->getCodeCompletionDelayedDeclState ();
188
258
189
259
auto &SM = CI.getSourceMgr ();
190
- if (SM. getIdentifierForBuffer (SM. getCodeCompletionBufferID ()) !=
191
- completionBuffer-> getBufferIdentifier () )
260
+ auto bufferName = completionBuffer-> getBufferIdentifier ();
261
+ if (SM. getIdentifierForBuffer (SM. getCodeCompletionBufferID ()) != bufferName )
192
262
return false ;
193
263
264
+ if (shouldCheckDependencies ()) {
265
+ if (areAnyDependentFilesInvalidated (CI, *FileSystem, bufferName,
266
+ DependencyCheckedTimestamp))
267
+ return false ;
268
+ DependencyCheckedTimestamp = std::chrono::system_clock::now ();
269
+ }
270
+
194
271
// Parse the new buffer into temporary SourceFile.
195
272
SourceManager tmpSM;
196
273
auto tmpBufferID = tmpSM.addMemBufferCopy (completionBuffer);
@@ -265,8 +342,7 @@ bool CompletionInstance::performCachedOperationIfPossible(
265
342
completionBuffer->getBuffer ().slice (startOffset, endOffset);
266
343
auto newOffset = Offset - startOffset;
267
344
268
- newBufferID = SM.addMemBufferCopy (sourceText,
269
- completionBuffer->getBufferIdentifier ());
345
+ newBufferID = SM.addMemBufferCopy (sourceText, bufferName);
270
346
SM.openVirtualFile (SM.getLocForBufferStart (newBufferID),
271
347
tmpSM.getDisplayNameForLoc (startLoc),
272
348
tmpSM.getLineAndColumn (startLoc).first - 1 );
@@ -312,8 +388,7 @@ bool CompletionInstance::performCachedOperationIfPossible(
312
388
endOffset = tmpSM.getLocOffsetInBuffer (endLoc, tmpBufferID);
313
389
sourceText = sourceText.slice (0 , endOffset);
314
390
}
315
- newBufferID = SM.addMemBufferCopy (sourceText,
316
- completionBuffer->getBufferIdentifier ());
391
+ newBufferID = SM.addMemBufferCopy (sourceText, bufferName);
317
392
SM.setCodeCompletionPoint (newBufferID, Offset);
318
393
319
394
// Create a new module and a source file using the current AST context.
@@ -372,7 +447,15 @@ bool CompletionInstance::performNewOperation(
372
447
llvm::function_ref<void (CompilerInstance &, bool )> Callback) {
373
448
llvm::PrettyStackTraceString trace (" While performing new completion" );
374
449
450
+ auto isCachedCompletionRequested = ArgsHash.hasValue ();
451
+
375
452
auto TheInstance = std::make_unique<CompilerInstance>();
453
+
454
+ // Track dependencies in fast-completion mode to invalidate the compiler
455
+ // instance if any dependent files are modified.
456
+ if (isCachedCompletionRequested)
457
+ TheInstance->createDependencyTracker (false );
458
+
376
459
{
377
460
auto &CI = *TheInstance;
378
461
if (DiagC)
@@ -410,15 +493,34 @@ bool CompletionInstance::performNewOperation(
410
493
Callback (CI, /* reusingASTContext=*/ false );
411
494
}
412
495
413
- if (ArgsHash.hasValue ()) {
414
- CachedCI = std::move (TheInstance);
415
- CachedArgHash = *ArgsHash;
416
- CachedReuseCount = 0 ;
417
- }
496
+ // Cache the compiler instance if fast completion is enabled.
497
+ if (isCachedCompletionRequested)
498
+ cacheCompilerInstance (std::move (TheInstance), *ArgsHash);
418
499
419
500
return true ;
420
501
}
421
502
503
+ void CompletionInstance::cacheCompilerInstance (
504
+ std::unique_ptr<CompilerInstance> CI, llvm::hash_code ArgsHash) {
505
+ CachedCI = std::move (CI);
506
+ CachedArgHash = ArgsHash;
507
+ auto now = std::chrono::system_clock::now ();
508
+ DependencyCheckedTimestamp = now;
509
+ CachedReuseCount = 0 ;
510
+ }
511
+
512
+ bool CompletionInstance::shouldCheckDependencies () const {
513
+ assert (CachedCI);
514
+ using namespace std ::chrono;
515
+ auto now = system_clock::now ();
516
+ return DependencyCheckedTimestamp + seconds (DependencyCheckIntervalSecond) < now;
517
+ }
518
+
519
+ void CompletionInstance::setDependencyCheckIntervalSecond (unsigned Value) {
520
+ std::lock_guard<std::mutex> lock (mtx);
521
+ DependencyCheckIntervalSecond = Value;
522
+ }
523
+
422
524
bool swift::ide::CompletionInstance::performOperation (
423
525
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
424
526
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
@@ -454,8 +556,9 @@ bool swift::ide::CompletionInstance::performOperation(
454
556
// the cached completion instance.
455
557
std::lock_guard<std::mutex> lock (mtx);
456
558
457
- if (performCachedOperationIfPossible (Invocation, ArgsHash, completionBuffer,
458
- Offset, DiagC, Callback))
559
+ if (performCachedOperationIfPossible (Invocation, ArgsHash, FileSystem,
560
+ completionBuffer, Offset, DiagC,
561
+ Callback))
459
562
return true ;
460
563
461
564
if (performNewOperation (ArgsHash, Invocation, FileSystem, completionBuffer,
0 commit comments