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,
473473 });
474474}
475475
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+
476483} // namespace clangd
477484} // namespace clang
Original file line number Diff line number Diff line change @@ -148,6 +148,21 @@ std::string getQualification(ASTContext &Context,
148148 const NamedDecl *ND,
149149 llvm::ArrayRef<std::string> VisibleNamespaces);
150150
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+
151166} // namespace clangd
152167} // namespace clang
153168
Original file line number Diff line number Diff line change @@ -489,6 +489,9 @@ llvm::Optional<SymbolID> getSymbolID(const CodeCompletionResult &R,
489489 switch (R.Kind ) {
490490 case CodeCompletionResult::RK_Declaration:
491491 case CodeCompletionResult::RK_Pattern: {
492+ // Computing USR caches linkage, which may change after code completion.
493+ if (hasUnstableLinkage (R.Declaration ))
494+ return llvm::None;
492495 return clang::clangd::getSymbolID (R.Declaration );
493496 }
494497 case CodeCompletionResult::RK_Macro:
@@ -1001,10 +1004,12 @@ class SignatureHelpCollector final : public CodeCompleteConsumer {
10011004 ScoredSignature Result;
10021005 Result.Signature = std::move (Signature);
10031006 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+ }
10081013 return Result;
10091014 }
10101015
Original file line number Diff line number Diff line change @@ -275,8 +275,9 @@ computeScope(const NamedDecl *D) {
275275 }
276276 if (InClass)
277277 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)
280281 return SymbolRelevanceSignals::FileScope;
281282 return SymbolRelevanceSignals::GlobalScope;
282283}
Original file line number Diff line number Diff line change @@ -2681,6 +2681,17 @@ TEST(CompletionTest, DerivedMethodsAreAlwaysVisible) {
26812681 ElementsAre (AllOf (ReturnType (" int" ), Named (" size" ))));
26822682}
26832683
2684+ TEST (CompletionTest, NoCrashWithIncompleteLambda) {
2685+ auto Completions = completions (" auto&& x = []{^" ).Completions ;
2686+ // The completion of x itself can cause a problem: in the code completion
2687+ // callback, its type is not known, which affects the linkage calculation.
2688+ // A bad linkage value gets cached, and subsequently updated.
2689+ EXPECT_THAT (Completions, Contains (Named (" x" )));
2690+
2691+ auto Signatures = signatures (" auto x() { x(^" ).signatures ;
2692+ EXPECT_THAT (Signatures, Contains (Sig (" x() -> auto" )));
2693+ }
2694+
26842695TEST (NoCompileCompletionTest, Basic) {
26852696 auto Results = completionsNoCompile (R"cpp(
26862697 void func() {
You can’t perform that action at this time.
0 commit comments