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"
23
21
#include " swift/Basic/LangOptions.h"
24
22
#include " swift/Basic/PrettyStackTrace.h"
25
23
#include " swift/Basic/SourceManager.h"
24
+ #include " swift/ClangImporter/ClangModule.h"
26
25
#include " swift/Driver/FrontendUtil.h"
27
26
#include " swift/Frontend/Frontend.h"
28
27
#include " swift/Parse/Lexer.h"
29
28
#include " swift/Parse/PersistentParserState.h"
29
+ #include " swift/Serialization/SerializedModuleLoader.h"
30
30
#include " swift/Subsystems.h"
31
+ #include " clang/AST/ASTContext.h"
31
32
#include " llvm/ADT/Hashing.h"
32
33
#include " llvm/Support/MemoryBuffer.h"
33
- #include " clang/AST/ASTContext.h"
34
34
35
35
using namespace swift ;
36
36
using namespace ide ;
@@ -165,72 +165,113 @@ static DeclContext *getEquivalentDeclContextFromSourceFile(DeclContext *DC,
165
165
return newDC;
166
166
}
167
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
-
168
+ // / For each dependency file in \p CI, run \p callback until the callback
169
+ // / returns \c true. Returns \c true if any callback call returns \c true, \c
170
+ // / false otherwise.
171
+ static bool
172
+ forEachDependencyUntilTrue (CompilerInstance &CI, ModuleDecl *CurrentModule,
173
+ unsigned excludeBufferID,
174
+ llvm::function_ref<bool (StringRef)> callback) {
205
175
// Check files in the current module.
206
- for (FileUnit *file : CI. getMainModule () ->getFiles ()) {
176
+ for (FileUnit *file : CurrentModule ->getFiles ()) {
207
177
StringRef filename;
208
- if (auto SF = dyn_cast<SourceFile>(file))
178
+ if (auto SF = dyn_cast<SourceFile>(file)) {
179
+ if (SF->getBufferID () == excludeBufferID)
180
+ continue ;
209
181
filename = SF->getFilename ();
210
- else if (auto LF = dyn_cast<LoadedFile>(file))
182
+ } else if (auto LF = dyn_cast<LoadedFile>(file))
211
183
filename = LF->getFilename ();
212
184
else
213
185
continue ;
214
186
215
- // Ignore the current file and synthesized files.
216
- if (filename.empty () || filename.front () == ' <' ||
217
- filename.equals (currentFileName))
187
+ // Ignore synthesized files.
188
+ if (filename.empty () || filename.front () == ' <' )
218
189
continue ;
219
190
220
- if (isInvalidated (filename))
191
+ if (callback (filename))
221
192
return true ;
222
193
}
223
194
224
195
// Check other non-system depenencies (e.g. modules, headers).
225
196
for (auto &dep : CI.getDependencyTracker ()->getDependencies ()) {
226
- if (isInvalidated (dep))
197
+ if (callback (dep))
227
198
return true ;
228
199
}
229
200
230
- // All loaded module files are not modified since the timestamp.
231
201
return false ;
232
202
}
233
203
204
+ // / Collect hash codes of the dependencies into \c Map.
205
+ static void cacheDependencyHashIfNeeded (CompilerInstance &CI,
206
+ ModuleDecl *CurrentModule,
207
+ unsigned excludeBufferID,
208
+ llvm::StringMap<llvm::hash_code> &Map) {
209
+ auto &FS = CI.getFileSystem ();
210
+ forEachDependencyUntilTrue (
211
+ CI, CurrentModule, excludeBufferID, [&](StringRef filename) {
212
+ if (Map.count (filename))
213
+ return false ;
214
+
215
+ auto stat = FS.status (filename);
216
+ if (!stat)
217
+ return false ;
218
+
219
+ // We will check the hash only if the modification time of the dependecy
220
+ // is zero. See 'areAnyDependentFilesInvalidated() below'.
221
+ if (stat->getLastModificationTime () != llvm::sys::TimePoint<>())
222
+ return false ;
223
+
224
+ auto buf = FS.getBufferForFile (filename);
225
+ Map[filename] = llvm::hash_value (buf.get ()->getBuffer ());
226
+ return false ;
227
+ });
228
+ }
229
+
230
+ // / Check if any dependent files are modified since \p timestamp.
231
+ static bool areAnyDependentFilesInvalidated (
232
+ CompilerInstance &CI, ModuleDecl *CurrentModule, llvm::vfs::FileSystem &FS,
233
+ unsigned excludeBufferID, llvm::sys::TimePoint<> timestamp,
234
+ llvm::StringMap<llvm::hash_code> &Map) {
235
+
236
+ return forEachDependencyUntilTrue (
237
+ CI, CurrentModule, excludeBufferID, [&](StringRef filePath) {
238
+ auto stat = FS.status (filePath);
239
+ if (!stat)
240
+ // Missing.
241
+ return true ;
242
+
243
+ auto lastModTime = stat->getLastModificationTime ();
244
+ if (lastModTime > timestamp)
245
+ // Modified.
246
+ return true ;
247
+
248
+ // If the last modification time is zero, this file is probably from a
249
+ // virtual file system. We need to check the content.
250
+ if (lastModTime == llvm::sys::TimePoint<>()) {
251
+ // Get the hash code of the last content.
252
+ auto oldHashEntry = Map.find (filePath);
253
+ if (oldHashEntry == Map.end ())
254
+ // Unreachable? Not virtual in old filesystem, but virtual in new
255
+ // one.
256
+ return true ;
257
+ auto oldHash = oldHashEntry->second ;
258
+
259
+ // Calculate the hash code of the current content.
260
+ auto newContent = FS.getBufferForFile (filePath);
261
+ if (!newContent)
262
+ // Unreachable? stat succeeded, but coundn't get the content.
263
+ return true ;
264
+
265
+ auto newHash = llvm::hash_value (newContent.get ()->getBuffer ());
266
+
267
+ if (oldHash != newHash)
268
+ return true ;
269
+ }
270
+
271
+ return false ;
272
+ });
273
+ }
274
+
234
275
} // namespace
235
276
236
277
bool CompletionInstance::performCachedOperationIfPossible (
@@ -262,8 +303,9 @@ bool CompletionInstance::performCachedOperationIfPossible(
262
303
return false ;
263
304
264
305
if (shouldCheckDependencies ()) {
265
- if (areAnyDependentFilesInvalidated (CI, *FileSystem, bufferName,
266
- DependencyCheckedTimestamp))
306
+ if (areAnyDependentFilesInvalidated (
307
+ CI, CurrentModule, *FileSystem, SM.getCodeCompletionBufferID (),
308
+ DependencyCheckedTimestamp, InMemoryDependencyHash))
267
309
return false ;
268
310
DependencyCheckedTimestamp = std::chrono::system_clock::now ();
269
311
}
@@ -406,6 +448,7 @@ bool CompletionInstance::performCachedOperationIfPossible(
406
448
performImportResolution (*newSF);
407
449
bindExtensions (*newSF);
408
450
451
+ CurrentModule = newM;
409
452
traceDC = newM;
410
453
#ifndef NDEBUG
411
454
const auto *reparsedState = newSF->getDelayedParserState ();
@@ -432,6 +475,8 @@ bool CompletionInstance::performCachedOperationIfPossible(
432
475
}
433
476
434
477
CachedReuseCount += 1 ;
478
+ cacheDependencyHashIfNeeded (CI, CurrentModule, SM.getCodeCompletionBufferID (),
479
+ InMemoryDependencyHash);
435
480
436
481
return true ;
437
482
}
@@ -500,17 +545,24 @@ bool CompletionInstance::performNewOperation(
500
545
void CompletionInstance::cacheCompilerInstance (
501
546
std::unique_ptr<CompilerInstance> CI, llvm::hash_code ArgsHash) {
502
547
CachedCI = std::move (CI);
548
+ CurrentModule = CachedCI->getMainModule ();
503
549
CachedArgHash = ArgsHash;
504
550
auto now = std::chrono::system_clock::now ();
505
551
DependencyCheckedTimestamp = now;
506
552
CachedReuseCount = 0 ;
553
+ InMemoryDependencyHash.clear ();
554
+ cacheDependencyHashIfNeeded (
555
+ *CachedCI, CurrentModule,
556
+ CachedCI->getASTContext ().SourceMgr .getCodeCompletionBufferID (),
557
+ InMemoryDependencyHash);
507
558
}
508
559
509
560
bool CompletionInstance::shouldCheckDependencies () const {
510
561
assert (CachedCI);
511
562
using namespace std ::chrono;
512
563
auto now = system_clock::now ();
513
- return DependencyCheckedTimestamp + seconds (DependencyCheckIntervalSecond) < now;
564
+ return DependencyCheckedTimestamp + seconds (DependencyCheckIntervalSecond) <
565
+ now;
514
566
}
515
567
516
568
void CompletionInstance::setDependencyCheckIntervalSecond (unsigned Value) {
0 commit comments