ilya-biryukov created this revision. ilya-biryukov added a reviewer: sammccall. Herald added subscribers: usaxena95, kadircet, arphaman. Herald added a project: All. ilya-biryukov requested review of this revision. Herald added a subscriber: MaskRay. Herald added projects: clang, clang-tools-extra.
Add support for concepts and requires expression in the clang index. Genarate USRs for concepts. Also how `RecursiveASTVisitor` handles return type requirement in requires expressions. The new code unpacks the synthetic template parameter list used for storing the actual expression. This simplifies implementation of the indexing. No code seems to depend on the original traversal anyway and the synthesized template parameter list is easily accessible from inside the requires expression if needed. Add tests in the clangd codebase. Fixes https://github.com/clangd/clangd/issues/1103. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D124441 Files: clang-tools-extra/clangd/CodeComplete.cpp clang-tools-extra/clangd/Protocol.cpp clang-tools-extra/clangd/Quality.cpp clang-tools-extra/clangd/index/SymbolCollector.cpp clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp clang-tools-extra/clangd/unittests/TestIndex.cpp clang-tools-extra/clangd/unittests/TestIndex.h clang-tools-extra/clangd/unittests/XRefsTests.cpp clang/include/clang/AST/RecursiveASTVisitor.h clang/include/clang/Index/IndexSymbol.h clang/lib/Index/IndexBody.cpp clang/lib/Index/IndexDecl.cpp clang/lib/Index/IndexSymbol.cpp clang/lib/Index/IndexTypeSourceInfo.cpp clang/lib/Index/IndexingContext.cpp clang/lib/Index/USRGeneration.cpp clang/tools/libclang/CXIndexDataConsumer.cpp
Index: clang/tools/libclang/CXIndexDataConsumer.cpp =================================================================== --- clang/tools/libclang/CXIndexDataConsumer.cpp +++ clang/tools/libclang/CXIndexDataConsumer.cpp @@ -1247,6 +1247,7 @@ case SymbolKind::TemplateTypeParm: case SymbolKind::TemplateTemplateParm: case SymbolKind::NonTypeTemplateParm: + case SymbolKind::Concept: return CXIdxEntity_Unexposed; case SymbolKind::Enum: return CXIdxEntity_Enum; Index: clang/lib/Index/USRGeneration.cpp =================================================================== --- clang/lib/Index/USRGeneration.cpp +++ clang/lib/Index/USRGeneration.cpp @@ -103,6 +103,7 @@ void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D); void VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D); void VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D); + void VisitConceptDecl(const ConceptDecl *D); void VisitLinkageSpecDecl(const LinkageSpecDecl *D) { IgnoreResults = true; // No USRs for linkage specs themselves. @@ -1007,7 +1008,13 @@ Out << D->getName(); // Simple name. } - +void USRGenerator::VisitConceptDecl(const ConceptDecl *D) { + if (ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + return; + VisitDeclContext(D->getDeclContext()); + Out << "@CT@"; + EmitDeclName(D); +} //===----------------------------------------------------------------------===// // USR generation functions. Index: clang/lib/Index/IndexingContext.cpp =================================================================== --- clang/lib/Index/IndexingContext.cpp +++ clang/lib/Index/IndexingContext.cpp @@ -86,7 +86,6 @@ isa<TemplateTemplateParmDecl>(D))) { return true; } - return handleDeclOccurrence(D, Loc, /*IsRef=*/true, Parent, Roles, Relations, RefE, RefD, DC); } @@ -264,7 +263,8 @@ isa<FieldDecl>(D) || isa<MSPropertyDecl>(D) || isa<ObjCImplDecl>(D) || - isa<ObjCPropertyImplDecl>(D)) + isa<ObjCPropertyImplDecl>(D) || + isa<ConceptDecl>(D)) return true; return false; Index: clang/lib/Index/IndexTypeSourceInfo.cpp =================================================================== --- clang/lib/Index/IndexTypeSourceInfo.cpp +++ clang/lib/Index/IndexTypeSourceInfo.cpp @@ -7,7 +7,10 @@ //===----------------------------------------------------------------------===// #include "IndexingContext.h" +#include "clang/AST/ASTConcept.h" +#include "clang/AST/PrettyPrinter.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/TypeLoc.h" #include "llvm/ADT/ScopeExit.h" using namespace clang; @@ -77,6 +80,13 @@ return true; } + bool VisitAutoTypeLoc(AutoTypeLoc TL) { + if (auto *C = TL.getNamedConcept()) + return IndexCtx.handleReference(C, TL.getConceptNameLoc(), Parent, + ParentDC); + return true; + } + bool traverseParamVarHelper(ParmVarDecl *D) { TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); if (D->getTypeSourceInfo()) Index: clang/lib/Index/IndexSymbol.cpp =================================================================== --- clang/lib/Index/IndexSymbol.cpp +++ clang/lib/Index/IndexSymbol.cpp @@ -371,6 +371,9 @@ case Decl::NonTypeTemplateParm: Info.Kind = SymbolKind::NonTypeTemplateParm; break; + case Decl::Concept: + Info.Kind = SymbolKind::Concept; + break; // Other decls get the 'unknown' kind. default: break; @@ -534,6 +537,7 @@ case SymbolKind::TemplateTypeParm: return "template-type-param"; case SymbolKind::TemplateTemplateParm: return "template-template-param"; case SymbolKind::NonTypeTemplateParm: return "non-type-template-param"; + case SymbolKind::Concept: return "concept"; } llvm_unreachable("invalid symbol kind"); } Index: clang/lib/Index/IndexDecl.cpp =================================================================== --- clang/lib/Index/IndexDecl.cpp +++ clang/lib/Index/IndexDecl.cpp @@ -7,10 +7,13 @@ //===----------------------------------------------------------------------===// #include "IndexingContext.h" +#include "clang/AST/ASTConcept.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" #include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexSymbol.h" using namespace clang; using namespace index; @@ -129,6 +132,8 @@ } } } + if (auto *C = D->getTrailingRequiresClause()) + IndexCtx.indexBody(C, Parent); } bool handleObjCMethod(const ObjCMethodDecl *D, @@ -681,36 +686,52 @@ return true; } - bool VisitTemplateDecl(const TemplateDecl *D) { + void indexTemplateParameters(TemplateParameterList *Params, + const NamedDecl *Parent) { + for (const NamedDecl *TP : *Params) { + if (IndexCtx.shouldIndexTemplateParameters()) + IndexCtx.handleDecl(TP); + if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(TP)) { + if (TTP->hasDefaultArgument()) + IndexCtx.indexTypeSourceInfo(TTP->getDefaultArgumentInfo(), Parent); + if (auto *C = TTP->getTypeConstraint()) + IndexCtx.handleReference(C->getNamedConcept(), C->getConceptNameLoc(), + Parent, TTP->getLexicalDeclContext()); + } else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(TP)) { + if (NTTP->hasDefaultArgument()) + IndexCtx.indexBody(NTTP->getDefaultArgument(), Parent); + } else if (const auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(TP)) { + if (TTPD->hasDefaultArgument()) + handleTemplateArgumentLoc(TTPD->getDefaultArgument(), Parent, + TP->getLexicalDeclContext()); + } + } + if (auto *R = Params->getRequiresClause()) + IndexCtx.indexBody(R, Parent); + } + bool VisitTemplateDecl(const TemplateDecl *D) { const NamedDecl *Parent = D->getTemplatedDecl(); if (!Parent) return true; // Index the default values for the template parameters. - if (D->getTemplateParameters() && - shouldIndexTemplateParameterDefaultValue(Parent)) { - const TemplateParameterList *Params = D->getTemplateParameters(); - for (const NamedDecl *TP : *Params) { - if (IndexCtx.shouldIndexTemplateParameters()) - IndexCtx.handleDecl(TP); - if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(TP)) { - if (TTP->hasDefaultArgument()) - IndexCtx.indexTypeSourceInfo(TTP->getDefaultArgumentInfo(), Parent); - } else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(TP)) { - if (NTTP->hasDefaultArgument()) - IndexCtx.indexBody(NTTP->getDefaultArgument(), Parent); - } else if (const auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(TP)) { - if (TTPD->hasDefaultArgument()) - handleTemplateArgumentLoc(TTPD->getDefaultArgument(), Parent, - TP->getLexicalDeclContext()); - } - } + auto *Params = D->getTemplateParameters(); + if (Params && shouldIndexTemplateParameterDefaultValue(Parent)) { + indexTemplateParameters(Params, Parent); } return Visit(Parent); } + bool VisitConceptDecl(const ConceptDecl *D) { + if (auto *Params = D->getTemplateParameters()) + indexTemplateParameters(Params, D); + if (auto *E = D->getConstraintExpr()) + IndexCtx.indexBody(E, D); + return IndexCtx.handleDecl(D); + } + bool VisitFriendDecl(const FriendDecl *D) { if (auto ND = D->getFriendDecl()) { // FIXME: Ignore a class template in a dependent context, these are not Index: clang/lib/Index/IndexBody.cpp =================================================================== --- clang/lib/Index/IndexBody.cpp +++ clang/lib/Index/IndexBody.cpp @@ -7,8 +7,12 @@ //===----------------------------------------------------------------------===// #include "IndexingContext.h" -#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/ASTConcept.h" #include "clang/AST/ASTLambda.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprConcepts.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Type.h" using namespace clang; using namespace clang::index; @@ -455,10 +459,10 @@ } bool VisitParmVarDecl(ParmVarDecl* D) { - // Index the parameters of lambda expression. + // Index the parameters of lambda expression and requires expression. if (IndexCtx.shouldIndexFunctionLocalSymbols()) { const auto *DC = D->getDeclContext(); - if (DC && isLambdaCallOperator(DC)) + if (DC && (isLambdaCallOperator(DC) || isa<RequiresExprBodyDecl>(DC))) IndexCtx.handleDecl(D); } return true; @@ -472,6 +476,21 @@ Relations, E); return true; } + + bool VisitConceptSpecializationExpr(ConceptSpecializationExpr *R) { + IndexCtx.handleReference(R->getNamedConcept(), R->getConceptNameLoc(), + Parent, ParentDC); + return true; + } + + bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { + // This handles references in return type requirements of RequiresExpr. + // E.g. `requires (T x) { {*x} -> ConceptRef }` + if (auto *C = D->getTypeConstraint()) + IndexCtx.handleReference(C->getNamedConcept(), C->getConceptNameLoc(), + Parent, ParentDC); + return true; + } }; } // anonymous namespace Index: clang/include/clang/Index/IndexSymbol.h =================================================================== --- clang/include/clang/Index/IndexSymbol.h +++ clang/include/clang/Index/IndexSymbol.h @@ -57,6 +57,8 @@ TemplateTypeParm, TemplateTemplateParm, NonTypeTemplateParm, + + Concept, /// C++20 concept. }; enum class SymbolLanguage : uint8_t { Index: clang/include/clang/AST/RecursiveASTVisitor.h =================================================================== --- clang/include/clang/AST/RecursiveASTVisitor.h +++ clang/include/clang/AST/RecursiveASTVisitor.h @@ -2841,9 +2841,10 @@ if (!ExprReq->isExprSubstitutionFailure()) TRY_TO(TraverseStmt(ExprReq->getExpr())); auto &RetReq = ExprReq->getReturnTypeRequirement(); - if (RetReq.isTypeConstraint()) - TRY_TO(TraverseTemplateParameterListHelper( - RetReq.getTypeConstraintTemplateParameterList())); + if (RetReq.isTypeConstraint()) { + TRY_TO(TraverseStmt( + RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint())); + } } else { auto *NestedReq = cast<concepts::NestedRequirement>(Req); if (!NestedReq->isSubstitutionFailure()) Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -1847,9 +1847,12 @@ } } -void checkFindRefs(llvm::StringRef Test, bool UseIndex = false) { +void checkFindRefs(llvm::StringRef Test, bool UseIndex = false, + std::vector<std::string> ExtraArgs = {}) { Annotations T(Test); auto TU = TestTU::withCode(T.code()); + TU.ExtraArgs = ExtraArgs; + auto AST = TU.build(); std::vector<Matcher<ReferencesResult::Reference>> ExpectedLocations; for (const auto &R : T.ranges()) @@ -2064,6 +2067,38 @@ checkFindRefs(Test); } +TEST(FindReferences, ConceptsWithinAST) { + constexpr llvm::StringLiteral Code = R"cpp( + template <class T> + concept $def[[IsSmal^l]] = sizeof(T) <= 8; + + template <class T> + concept IsSmallPtr = requires(T x) { + { *x } -> [[IsSmal^l]]; + }; + + [[IsSmall]] auto i = 8; + template<[[IsSmal^l]] U> void foo(); + template<class U> void bar() requires [[IsSmal^l]]<U>; + template<class U> requires [[IsSmal^l]]<U> void baz(); + static_assert([[IsSma^ll]]<int>); + )cpp"; + checkFindRefs(Code, false, {"-std=c++20"}); +} + +TEST(FindReferences, RequiresExprParameters) { + constexpr llvm::StringLiteral Code = R"cpp( + template <class T> + concept IsSmall = sizeof(T) <= 8; + + template <class T> + concept IsSmallPtr = requires(T $def[[^x]]) { + { *[[^x]] } -> IsSmall; + }; + )cpp"; + checkFindRefs(Code, false, {"-std=c++20"}); +} + TEST(FindReferences, IncludeOverrides) { llvm::StringRef Test = R"cpp( Index: clang-tools-extra/clangd/unittests/TestIndex.h =================================================================== --- clang-tools-extra/clangd/unittests/TestIndex.h +++ clang-tools-extra/clangd/unittests/TestIndex.h @@ -31,6 +31,8 @@ Symbol var(llvm::StringRef Name); // Creates a namespace symbol. Symbol ns(llvm::StringRef Name); +// Create a C++20 concept symbol. +Symbol conceptSym(llvm::StringRef Name); // Create a slab of symbols with the given qualified names as IDs and names. SymbolSlab generateSymbols(std::vector<std::string> QualifiedNames); Index: clang-tools-extra/clangd/unittests/TestIndex.cpp =================================================================== --- clang-tools-extra/clangd/unittests/TestIndex.cpp +++ clang-tools-extra/clangd/unittests/TestIndex.cpp @@ -77,6 +77,10 @@ return sym(Name, index::SymbolKind::Namespace, "@N@\\0"); } +Symbol conceptSym(llvm::StringRef Name) { + return sym(Name, index::SymbolKind::Concept, "@CT@\\0"); +} + SymbolSlab generateSymbols(std::vector<std::string> QualifiedNames) { SymbolSlab::Builder Slab; for (llvm::StringRef QName : QualifiedNames) Index: clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp +++ clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp @@ -59,6 +59,7 @@ MATCHER_P(templateArgs, TemplArgs, "") { return arg.TemplateSpecializationArgs == TemplArgs; } +MATCHER_P(hasKind, Kind, "") { return arg.SymInfo.Kind == Kind; } MATCHER_P(declURI, P, "") { return StringRef(arg.CanonicalDeclaration.FileURI) == P; } @@ -1962,6 +1963,17 @@ EXPECT_THAT(Symbols, IsEmpty()); } +TEST_F(SymbolCollectorTest, Concepts) { + const char *Header = R"cpp( + template <class T> + concept A = sizeof(T) <= 8; + )cpp"; + runSymbolCollector("", Header, {"-std=c++20"}); + EXPECT_THAT(Symbols, + UnorderedElementsAre(AllOf( + qName("A"), hasKind(clang::index::SymbolKind::Concept)))); +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3565,6 +3565,36 @@ IsEmpty()); } +TEST(CompletionTest, Concepts) { + Annotations Code(R"cpp( + template<class T> + concept A = sizeof(T) <= 8; + + template<$tparam^A U> + int foo(); + + template<class T> + concept b = $expr^A<T> && $expr^sizeof(T) % 2 == 0 || $expr^A<T> && sizeof(T) == 1; + )cpp"); + TestTU TU; + TU.Code = Code.code().str(); + TU.ExtraArgs = {"-std=c++20"}; + + std::vector<Symbol> Syms = {conceptSym("same_as")}; + 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")))) + << "Completing template parameter at position " << P; + } + + for (auto P : Code.points("expr")) { + EXPECT_THAT(completions(TU, P, Syms).Completions, + AllOf(Contains(named("A")), Contains(named("same_as")))) + << "Completing 'requires' expression at position " << P; + } +} + TEST(SignatureHelp, DocFormat) { Annotations Code(R"cpp( // Comment `with` markup. Index: clang-tools-extra/clangd/index/SymbolCollector.cpp =================================================================== --- clang-tools-extra/clangd/index/SymbolCollector.cpp +++ clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -88,6 +88,7 @@ case SK::Function: case SK::Variable: case SK::EnumConstant: + case SK::Concept: return true; default: return false; Index: clang-tools-extra/clangd/Quality.cpp =================================================================== --- clang-tools-extra/clangd/Quality.cpp +++ clang-tools-extra/clangd/Quality.cpp @@ -122,6 +122,7 @@ case index::SymbolKind::TypeAlias: case index::SymbolKind::TemplateTypeParm: case index::SymbolKind::TemplateTemplateParm: + case index::SymbolKind::Concept: return SymbolQualitySignals::Type; case index::SymbolKind::Function: case index::SymbolKind::ClassMethod: Index: clang-tools-extra/clangd/Protocol.cpp =================================================================== --- clang-tools-extra/clangd/Protocol.cpp +++ clang-tools-extra/clangd/Protocol.cpp @@ -301,6 +301,8 @@ case index::SymbolKind::TemplateTemplateParm: case index::SymbolKind::TemplateTypeParm: return SymbolKind::TypeParameter; + case index::SymbolKind::Concept: + return SymbolKind::Interface; } llvm_unreachable("invalid symbol kind"); } Index: clang-tools-extra/clangd/CodeComplete.cpp =================================================================== --- clang-tools-extra/clangd/CodeComplete.cpp +++ clang-tools-extra/clangd/CodeComplete.cpp @@ -129,6 +129,8 @@ case SK::TemplateTypeParm: case SK::TemplateTemplateParm: return CompletionItemKind::TypeParameter; + case SK::Concept: + return CompletionItemKind::Interface; } llvm_unreachable("Unhandled clang::index::SymbolKind."); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits