@@ -276,6 +276,51 @@ static bool areAnyDependentFilesInvalidated(
276
276
});
277
277
}
278
278
279
+ // / Get interface hash of \p SF including the type members in the file.
280
+ // /
281
+ // / See if the inteface of the function and types visible from a function body
282
+ // / has changed since the last completion. If they haven't changed, completion
283
+ // / can reuse the existing AST of the source file. \c SF->getInterfaceHash() is
284
+ // / not enough because it doesn't take the interface of the type members into
285
+ // / account. For example:
286
+ // /
287
+ // / struct S {
288
+ // / func foo() {}
289
+ // / }
290
+ // / func main(val: S) {
291
+ // / val.<HERE>
292
+ // / }
293
+ // /
294
+ // / In this case, we need to ensure that the interface of \c S hasn't changed.
295
+ // / Note that we don't care about local types (i.e. type declarations inside
296
+ // / function bodies, closures, or top level statement bodies) because they are
297
+ // / not visible from other functions where the completion is happening.
298
+ void getInterfaceHashIncludingTypeMembers (SourceFile *SF,
299
+ llvm::SmallString<32 > &str) {
300
+ // / FIXME: Gross. Hashing multiple "hash" values.
301
+ llvm::MD5 hash;
302
+ SF->getInterfaceHash (str);
303
+ hash.update (str);
304
+
305
+ std::function<void (IterableDeclContext *)> hashTypeBodyFingerprints =
306
+ [&](IterableDeclContext *IDC) {
307
+ if (auto fp = IDC->getBodyFingerprint ())
308
+ hash.update (*fp);
309
+ for (auto *member : IDC->getParsedMembers ())
310
+ if (auto *childIDC = dyn_cast<IterableDeclContext>(member))
311
+ hashTypeBodyFingerprints (childIDC);
312
+ };
313
+
314
+ for (auto *D : SF->getTopLevelDecls ()) {
315
+ if (auto IDC = dyn_cast<IterableDeclContext>(D))
316
+ hashTypeBodyFingerprints (IDC);
317
+ }
318
+
319
+ llvm::MD5::MD5Result result;
320
+ hash.final (result);
321
+ str = result.digest ();
322
+ }
323
+
279
324
} // namespace
280
325
281
326
bool CompletionInstance::performCachedOperationIfPossible (
@@ -355,8 +400,8 @@ bool CompletionInstance::performCachedOperationIfPossible(
355
400
// If the interface has changed, AST must be refreshed.
356
401
llvm::SmallString<32 > oldInterfaceHash{};
357
402
llvm::SmallString<32 > newInterfaceHash{};
358
- oldSF-> getInterfaceHash ( oldInterfaceHash);
359
- tmpSF-> getInterfaceHash ( newInterfaceHash);
403
+ getInterfaceHashIncludingTypeMembers (oldSF, oldInterfaceHash);
404
+ getInterfaceHashIncludingTypeMembers (tmpSF, newInterfaceHash);
360
405
if (oldInterfaceHash != newInterfaceHash)
361
406
return false ;
362
407
@@ -406,6 +451,10 @@ bool CompletionInstance::performCachedOperationIfPossible(
406
451
Scope Top (SI, ScopeKind::TopLevel);
407
452
Scope Body (SI, ScopeKind::FunctionBody);
408
453
454
+ assert (oldInfo.Kind == CodeCompletionDelayedDeclKind::FunctionBody &&
455
+ " If the interface hash is the same as old one, the previous kind "
456
+ " must be FunctionBody too. Otherwise, hashing is too weak" );
457
+ oldInfo.Kind = CodeCompletionDelayedDeclKind::FunctionBody;
409
458
oldInfo.ParentContext = DC;
410
459
oldInfo.StartOffset = newInfo.StartOffset ;
411
460
oldInfo.EndOffset = newInfo.EndOffset ;
0 commit comments