File tree Expand file tree Collapse file tree 5 files changed +45
-6
lines changed Expand file tree Collapse file tree 5 files changed +45
-6
lines changed Original file line number Diff line number Diff line change @@ -473,5 +473,12 @@ std::string getQualification(ASTContext &Context,
473
473
});
474
474
}
475
475
476
+ bool hasUnstableLinkage(const Decl *D) {
477
+ // Linkage of a ValueDecl depends on the type.
478
+ // If that's not deduced yet, deducing it may change the linkage.
479
+ auto *VD = llvm::dyn_cast_or_null<ValueDecl>(D);
480
+ return VD && !VD->getType().isNull() && VD->getType()->isUndeducedType();
481
+ }
482
+
476
483
} // namespace clangd
477
484
} // namespace clang
Original file line number Diff line number Diff line change @@ -148,6 +148,21 @@ std::string getQualification(ASTContext &Context,
148
148
const NamedDecl *ND,
149
149
llvm::ArrayRef<std::string> VisibleNamespaces);
150
150
151
+ /// Whether we must avoid computing linkage for D during code completion.
152
+ /// Clang aggressively caches linkage computation, which is stable after the AST
153
+ /// is built. Unfortunately the AST is incomplete during code completion, so
154
+ /// linkage may still change.
155
+ ///
156
+ /// Example: `auto x = []{^}` at file scope.
157
+ /// During code completion, the initializer for x hasn't been parsed yet.
158
+ /// x has type `undeduced auto`, and external linkage.
159
+ /// If we compute linkage at this point, the external linkage will be cached.
160
+ ///
161
+ /// After code completion the initializer is attached, and x has a lambda type.
162
+ /// This means x has "unique external" linkage. If we computed linkage above,
163
+ /// the cached value is incorrect. (clang catches this with an assertion).
164
+ bool hasUnstableLinkage(const Decl *D);
165
+
151
166
} // namespace clangd
152
167
} // namespace clang
153
168
Original file line number Diff line number Diff line change @@ -489,6 +489,9 @@ llvm::Optional<SymbolID> getSymbolID(const CodeCompletionResult &R,
489
489
switch (R.Kind) {
490
490
case CodeCompletionResult::RK_Declaration:
491
491
case CodeCompletionResult::RK_Pattern: {
492
+ // Computing USR caches linkage, which may change after code completion.
493
+ if (hasUnstableLinkage(R.Declaration))
494
+ return llvm::None;
492
495
return clang::clangd::getSymbolID(R.Declaration);
493
496
}
494
497
case CodeCompletionResult::RK_Macro:
@@ -1001,10 +1004,12 @@ class SignatureHelpCollector final : public CodeCompleteConsumer {
1001
1004
ScoredSignature Result;
1002
1005
Result.Signature = std::move(Signature);
1003
1006
Result.Quality = Signal;
1004
- Result.IDForDoc =
1005
- Result.Signature.documentation.empty() && Candidate.getFunction()
1006
- ? clangd::getSymbolID(Candidate.getFunction())
1007
- : None;
1007
+ const FunctionDecl *Func = Candidate.getFunction();
1008
+ if (Func && Result.Signature.documentation.empty()) {
1009
+ // Computing USR caches linkage, which may change after code completion.
1010
+ if (!hasUnstableLinkage(Func))
1011
+ Result.IDForDoc = clangd::getSymbolID(Func);
1012
+ }
1008
1013
return Result;
1009
1014
}
1010
1015
Original file line number Diff line number Diff line change @@ -275,8 +275,9 @@ computeScope(const NamedDecl *D) {
275
275
}
276
276
if (InClass)
277
277
return SymbolRelevanceSignals::ClassScope;
278
- // This threshold could be tweaked, e.g. to treat module-visible as global.
279
- if (D->getLinkageInternal() < ExternalLinkage)
278
+ // ExternalLinkage threshold could be tweaked, e.g. module-visible as global.
279
+ // Avoid caching linkage if it may change after enclosing code completion.
280
+ if (hasUnstableLinkage(D) || D->getLinkageInternal() < ExternalLinkage)
280
281
return SymbolRelevanceSignals::FileScope;
281
282
return SymbolRelevanceSignals::GlobalScope;
282
283
}
Original file line number Diff line number Diff line change @@ -2664,6 +2664,17 @@ TEST(CompletionTest, DerivedMethodsAreAlwaysVisible) {
2664
2664
ElementsAre(AllOf(ReturnType("int"), Named("size"))));
2665
2665
}
2666
2666
2667
+ TEST(CompletionTest, NoCrashWithIncompleteLambda) {
2668
+ auto Completions = completions("auto&& x = []{^").Completions;
2669
+ // The completion of x itself can cause a problem: in the code completion
2670
+ // callback, its type is not known, which affects the linkage calculation.
2671
+ // A bad linkage value gets cached, and subsequently updated.
2672
+ EXPECT_THAT(Completions, Contains(Named("x")));
2673
+
2674
+ auto Signatures = signatures("auto x() { x(^").signatures;
2675
+ EXPECT_THAT(Signatures, Contains(Sig("x() -> auto")));
2676
+ }
2677
+
2667
2678
TEST(NoCompileCompletionTest, Basic) {
2668
2679
auto Results = completionsNoCompile(R"cpp(
2669
2680
void func() {
You can’t perform that action at this time.
0 commit comments