Skip to content

Commit 9f51c06

Browse files
authored
Merge pull request #66517 from ahoppen/ahoppen/iterator-invalidation
[CodeCompletion] Fix an issue that causes an iterator to be invalidated while iterating
2 parents 02c3678 + 0996342 commit 9f51c06

File tree

1 file changed

+48
-1
lines changed

1 file changed

+48
-1
lines changed

lib/IDE/CompletionLookup.cpp

+48-1
Original file line numberDiff line numberDiff line change
@@ -3215,16 +3215,61 @@ void CompletionLookup::getTypeCompletionsInDeclContext(SourceLoc Loc,
32153215
RequestedCachedResults.insert(RequestedResultsTy::topLevelResults(filter));
32163216
}
32173217

3218+
namespace {
3219+
3220+
/// A \c VisibleDeclConsumer that stores all decls that are found and is able
3221+
/// to forward the to another \c VisibleDeclConsumer later.
3222+
class StoringDeclConsumer : public VisibleDeclConsumer {
3223+
struct FoundDecl {
3224+
ValueDecl *VD;
3225+
DeclVisibilityKind Reason;
3226+
DynamicLookupInfo DynamicLookupInfo;
3227+
};
3228+
3229+
std::vector<FoundDecl> FoundDecls;
3230+
3231+
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
3232+
DynamicLookupInfo DynamicLookupInfo = {}) override {
3233+
FoundDecls.push_back({VD, Reason, DynamicLookupInfo});
3234+
}
3235+
3236+
public:
3237+
/// Call \c foundDecl for every declaration that this consumer has found.
3238+
void forward(VisibleDeclConsumer &Consumer) {
3239+
for (auto &FoundDecl : FoundDecls) {
3240+
Consumer.foundDecl(FoundDecl.VD, FoundDecl.Reason,
3241+
FoundDecl.DynamicLookupInfo);
3242+
}
3243+
}
3244+
};
3245+
3246+
} // namespace
3247+
32183248
void CompletionLookup::getToplevelCompletions(CodeCompletionFilter Filter) {
32193249
Kind = (Filter - CodeCompletionFilterFlag::Module)
32203250
.containsOnly(CodeCompletionFilterFlag::Type)
32213251
? LookupKind::TypeInDeclContext
32223252
: LookupKind::ValueInDeclContext;
32233253
NeedLeadingDot = false;
32243254

3255+
// If we have 'addinitstotoplevel' enabled, calling `foundDecl` on `this`
3256+
// can cause macros to get expanded, which can then cause new members ot get
3257+
// added to 'TopLevelValues', invalidating iterator over `TopLevelDecls` in
3258+
// `SourceLookupCache::lookupVisibleDecls`.
3259+
//
3260+
// Technically `foundDecl` should not expand macros or discover new top level
3261+
// members in any way because those newly discovered decls will not be added
3262+
// to the code completion results. However, it's preferrable to miss results
3263+
// than to silently invalidate a collection, resulting in hard-to-diagnose
3264+
// crashes.
3265+
// Thus, store all the decls found by `CurrModule->lookupVisibleDecls` in a
3266+
// vector first and only call `this->foundDecl` once we have left the
3267+
// iteration loop over `TopLevelDecls`.
3268+
StoringDeclConsumer StoringConsumer;
3269+
32253270
UsableFilteringDeclConsumer UsableFilteringConsumer(
32263271
Ctx.SourceMgr, CurrDeclContext, Ctx.SourceMgr.getIDEInspectionTargetLoc(),
3227-
*this);
3272+
StoringConsumer);
32283273
AccessFilteringDeclConsumer AccessFilteringConsumer(CurrDeclContext,
32293274
UsableFilteringConsumer);
32303275

@@ -3243,6 +3288,8 @@ void CompletionLookup::getToplevelCompletions(CodeCompletionFilter Filter) {
32433288

32443289
CurrModule->lookupVisibleDecls({}, FilteringConsumer,
32453290
NLKind::UnqualifiedLookup);
3291+
3292+
StoringConsumer.forward(*this);
32463293
}
32473294

32483295
void CompletionLookup::lookupExternalModuleDecls(

0 commit comments

Comments
 (0)