massberg created this revision. massberg added a reviewer: sammccall. Herald added subscribers: kadircet, arphaman. Herald added a project: All. massberg requested review of this revision. Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov. Herald added a project: clang-tools-extra.
In case of a top level context the first template argument of a concept should be dropped. Currently the indexer doesn't support different signatures for different contexts (for an index entry always the default `Symbol` context is used). Thus we add a hack which checks if we are in a top level context and have a concept and in that case removes the first argment of the signature and snippet suffix. If there is only a single argument, the signature and snippet suffix are completly removed. The check for the first argument is done by simply looking for the first comma which should be sufficient in most cases. Additionally extend test environment to support adding artificial index entries with signature and completion snippet suffix. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D154450 Files: clang-tools-extra/clangd/CodeComplete.cpp clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp clang-tools-extra/clangd/unittests/TestIndex.cpp clang-tools-extra/clangd/unittests/TestIndex.h
Index: clang-tools-extra/clangd/unittests/TestIndex.h =================================================================== --- clang-tools-extra/clangd/unittests/TestIndex.h +++ clang-tools-extra/clangd/unittests/TestIndex.h @@ -20,7 +20,8 @@ // Helpers to produce fake index symbols with proper SymbolID. // USRFormat is a regex replacement string for the unqualified part of the USR. Symbol sym(llvm::StringRef QName, index::SymbolKind Kind, - llvm::StringRef USRFormat); + llvm::StringRef USRFormat, llvm::StringRef Signature = "", + llvm::StringRef CompletionSnippetSuffix = ""); // Creats a function symbol assuming no function arg. Symbol func(llvm::StringRef Name); // Creates a class symbol. @@ -34,7 +35,8 @@ // Creates a namespace symbol. Symbol ns(llvm::StringRef Name); // Create a C++20 concept symbol. -Symbol conceptSym(llvm::StringRef Name); +Symbol conceptSym(llvm::StringRef Name, llvm::StringRef Signature = "", + llvm::StringRef CompletionSnippetSuffix = ""); // Create an Objective-C symbol. Symbol objcSym(llvm::StringRef Name, index::SymbolKind Kind, Index: clang-tools-extra/clangd/unittests/TestIndex.cpp =================================================================== --- clang-tools-extra/clangd/unittests/TestIndex.cpp +++ clang-tools-extra/clangd/unittests/TestIndex.cpp @@ -38,7 +38,8 @@ // Helpers to produce fake index symbols for memIndex() or completions(). // USRFormat is a regex replacement string for the unqualified part of the USR. Symbol sym(llvm::StringRef QName, index::SymbolKind Kind, - llvm::StringRef USRFormat) { + llvm::StringRef USRFormat, llvm::StringRef Signature, + llvm::StringRef CompletionSnippetSuffix) { Symbol Sym; std::string USR = "c:"; // We synthesize a few simple cases of USRs by hand! size_t Pos = QName.rfind("::"); @@ -55,6 +56,8 @@ Sym.SymInfo.Kind = Kind; Sym.Flags |= Symbol::IndexedForCodeCompletion; Sym.Origin = SymbolOrigin::Static; + Sym.Signature = Signature; + Sym.CompletionSnippetSuffix = CompletionSnippetSuffix; return Sym; } @@ -82,8 +85,10 @@ return sym(Name, index::SymbolKind::Namespace, "@N@\\0"); } -Symbol conceptSym(llvm::StringRef Name) { - return sym(Name, index::SymbolKind::Concept, "@CT@\\0"); +Symbol conceptSym(llvm::StringRef Name, llvm::StringRef Signature, + llvm::StringRef CompletionSnippetSuffix) { + return sym(Name, index::SymbolKind::Concept, "@CT@\\0", Signature, + CompletionSnippetSuffix); } Symbol objcSym(llvm::StringRef Name, index::SymbolKind Kind, Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3961,26 +3961,48 @@ template<$tparam^A U> int foo(); + template<U> + int bar() requires $other^A; + template<class T> - concept b = $other^A<T> && $other^sizeof(T) % 2 == 0 || $other^A<T> && sizeof(T) == 1; + concept b = $other^A && $other^sizeof(T) % 2 == 0 || $other^A && sizeof(T) == 1; - $other^A<T> auto i = 19; + $toplevel^A auto i = 19; )cpp"); TestTU TU; TU.Code = Code.code().str(); TU.ExtraArgs = {"-std=c++20"}; - std::vector<Symbol> Syms = {conceptSym("same_as")}; + std::vector<Symbol> Syms = { + conceptSym("same_as", "<typename Tp, typename Up>", + "<${1:typename Tp}, ${2:typename Up}>")}; for (auto P : Code.points("tparam")) { - ASSERT_THAT(completions(TU, P, Syms).Completions, - AllOf(Contains(named("A")), Contains(named("same_as")), - Contains(named("class")), Contains(named("typename")))) + ASSERT_THAT( + completions(TU, P, Syms).Completions, + AllOf(Contains(AllOf(named("A"), signature(""), snippetSuffix(""))), + Contains(AllOf(named("same_as"), signature("<typename Up>"), + snippetSuffix("<${2:typename Up}>"))), + Contains(named("class")), Contains(named("typename")))) << "Completing template parameter at position " << P; } + for (auto P : Code.points("toplevel")) { + EXPECT_THAT( + completions(TU, P, Syms).Completions, + AllOf(Contains(AllOf(named("A"), signature(""), snippetSuffix(""))), + Contains(AllOf(named("same_as"), signature("<typename Up>"), + snippetSuffix("<${2:typename Up}>"))))) + << "Completing 'requires' expression at position " << P; + } + for (auto P : Code.points("other")) { - EXPECT_THAT(completions(TU, P, Syms).Completions, - AllOf(Contains(named("A")), Contains(named("same_as")))) + EXPECT_THAT( + completions(TU, P, Syms).Completions, + AllOf(Contains(AllOf(named("A"), signature("<class T>"), + snippetSuffix("<${1:class T}>"))), + Contains(AllOf( + named("same_as"), signature("<typename Tp, typename Up>"), + snippetSuffix("<${1:typename Tp}, ${2:typename Up}>"))))) << "Completing 'requires' expression at position " << P; } } Index: clang-tools-extra/clangd/CodeComplete.cpp =================================================================== --- clang-tools-extra/clangd/CodeComplete.cpp +++ clang-tools-extra/clangd/CodeComplete.cpp @@ -316,6 +316,17 @@ } }; +// Remove the first template argument from Signature. +// If Signature only contains a single argument an empty string is returned. +std::string RemoveFirstTemplateArg(const std::string &Signature) { + if (Signature.empty() || Signature.front() != '<' || Signature.back() != '>') + return Signature; + std::size_t FirstComma = Signature.find(", "); + if (FirstComma == std::string::npos) + return ""; + return "<" + Signature.substr(FirstComma + 2); +} + // Assembles a code completion out of a bundle of >=1 completion candidates. // Many of the expensive strings are only computed at this point, once we know // the candidate bundle is going to be returned. @@ -336,7 +347,7 @@ EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets), IsUsingDeclaration(IsUsingDeclaration), NextTokenKind(NextTokenKind) { Completion.Deprecated = true; // cleared by any non-deprecated overload. - add(C, SemaCCS); + add(C, SemaCCS, ContextKind); if (C.SemaResult) { assert(ASTCtx); Completion.Origin |= SymbolOrigin::AST; @@ -443,21 +454,36 @@ }); } - void add(const CompletionCandidate &C, CodeCompletionString *SemaCCS) { + void add(const CompletionCandidate &C, CodeCompletionString *SemaCCS, + CodeCompletionContext::Kind ContextKind) { assert(bool(C.SemaResult) == bool(SemaCCS)); Bundled.emplace_back(); BundledEntry &S = Bundled.back(); + bool IsConcept = false; if (C.SemaResult) { getSignature(*SemaCCS, &S.Signature, &S.SnippetSuffix, C.SemaResult->Kind, C.SemaResult->CursorKind, &Completion.RequiredQualifier); if (!C.SemaResult->FunctionCanBeCall) S.SnippetSuffix.clear(); S.ReturnType = getReturnType(*SemaCCS); + if (C.SemaResult->Kind == CodeCompletionResult::RK_Declaration) + if (const auto *D = C.SemaResult->getDeclaration()) + if (isa<ConceptDecl>(D)) + IsConcept = true; } else if (C.IndexResult) { S.Signature = std::string(C.IndexResult->Signature); S.SnippetSuffix = std::string(C.IndexResult->CompletionSnippetSuffix); S.ReturnType = std::string(C.IndexResult->ReturnType); + if (C.IndexResult->SymInfo.Kind == index::SymbolKind::Concept) + IsConcept = true; } + + // Hack to remove first template argument in some special cases. + if (IsConcept && ContextKind == CodeCompletionContext::CCC_TopLevel) { + S.Signature = RemoveFirstTemplateArg(S.Signature); + S.SnippetSuffix = RemoveFirstTemplateArg(S.SnippetSuffix); + } + if (!Completion.Documentation) { auto SetDoc = [&](llvm::StringRef Doc) { if (!Doc.empty()) { @@ -2020,7 +2046,7 @@ Item, SemaCCS, AccessibleScopes, *Inserter, FileName, CCContextKind, Opts, IsUsingDeclaration, NextTokenKind); else - Builder->add(Item, SemaCCS); + Builder->add(Item, SemaCCS, CCContextKind); } return Builder->build(); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits