diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/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."); } diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/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"); } diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp --- a/clang-tools-extra/clangd/Quality.cpp +++ b/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: diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/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; diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3565,6 +3565,38 @@ IsEmpty()); } +TEST(CompletionTest, Concepts) { + Annotations Code(R"cpp( + template + concept A = sizeof(T) <= 8; + + template<$tparam^A U> + int foo(); + + template + concept b = $other^A && $other^sizeof(T) % 2 == 0 || $other^A && sizeof(T) == 1; + + $other^A auto i = 19; + )cpp"); + TestTU TU; + TU.Code = Code.code().str(); + TU.ExtraArgs = {"-std=c++20"}; + + std::vector 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("other")) { + 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. diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp --- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp +++ b/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 + 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 diff --git a/clang-tools-extra/clangd/unittests/TestIndex.h b/clang-tools-extra/clangd/unittests/TestIndex.h --- a/clang-tools-extra/clangd/unittests/TestIndex.h +++ b/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 QualifiedNames); diff --git a/clang-tools-extra/clangd/unittests/TestIndex.cpp b/clang-tools-extra/clangd/unittests/TestIndex.cpp --- a/clang-tools-extra/clangd/unittests/TestIndex.cpp +++ b/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 QualifiedNames) { SymbolSlab::Builder Slab; for (llvm::StringRef QName : QualifiedNames) diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -1850,6 +1850,8 @@ void checkFindRefs(llvm::StringRef Test, bool UseIndex = false) { Annotations T(Test); auto TU = TestTU::withCode(T.code()); + TU.ExtraArgs.push_back("-std=c++20"); + auto AST = TU.build(); std::vector> ExpectedLocations; for (const auto &R : T.ranges()) @@ -2064,6 +2066,38 @@ checkFindRefs(Test); } +TEST(FindReferences, ConceptsWithinAST) { + constexpr llvm::StringLiteral Code = R"cpp( + template + concept $def[[IsSmal^l]] = sizeof(T) <= 8; + + template + concept IsSmallPtr = requires(T x) { + { *x } -> [[IsSmal^l]]; + }; + + [[IsSmall]] auto i = 'c'; + template<[[IsSmal^l]] U> void foo(); + template void bar() requires [[IsSmal^l]]; + template requires [[IsSmal^l]] void baz(); + static_assert([[IsSma^ll]]); + )cpp"; + checkFindRefs(Code); +} + +TEST(FindReferences, RequiresExprParameters) { + constexpr llvm::StringLiteral Code = R"cpp( + template + concept IsSmall = sizeof(T) <= 8; + + template + concept IsSmallPtr = requires(T $def[[^x]]) { + { *[[^x]] } -> IsSmall; + }; + )cpp"; + checkFindRefs(Code); +} + TEST(FindReferences, IncludeOverrides) { llvm::StringRef Test = R"cpp( diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/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(Req); if (!NestedReq->isSubstitutionFailure()) diff --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h --- a/clang/include/clang/Index/IndexSymbol.h +++ b/clang/include/clang/Index/IndexSymbol.h @@ -57,6 +57,8 @@ TemplateTypeParm, TemplateTemplateParm, NonTypeTemplateParm, + + Concept, /// C++20 concept. }; enum class SymbolLanguage : uint8_t { diff --git a/clang/lib/Index/IndexBody.cpp b/clang/lib/Index/IndexBody.cpp --- a/clang/lib/Index/IndexBody.cpp +++ b/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(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 diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp --- a/clang/lib/Index/IndexDecl.cpp +++ b/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(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(TP)) { + if (NTTP->hasDefaultArgument()) + IndexCtx.indexBody(NTTP->getDefaultArgument(), Parent); + } else if (const auto *TTPD = dyn_cast(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(TP)) { - if (TTP->hasDefaultArgument()) - IndexCtx.indexTypeSourceInfo(TTP->getDefaultArgumentInfo(), Parent); - } else if (const auto *NTTP = dyn_cast(TP)) { - if (NTTP->hasDefaultArgument()) - IndexCtx.indexBody(NTTP->getDefaultArgument(), Parent); - } else if (const auto *TTPD = dyn_cast(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 diff --git a/clang/lib/Index/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp --- a/clang/lib/Index/IndexSymbol.cpp +++ b/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"); } diff --git a/clang/lib/Index/IndexTypeSourceInfo.cpp b/clang/lib/Index/IndexTypeSourceInfo.cpp --- a/clang/lib/Index/IndexTypeSourceInfo.cpp +++ b/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()) diff --git a/clang/lib/Index/IndexingContext.cpp b/clang/lib/Index/IndexingContext.cpp --- a/clang/lib/Index/IndexingContext.cpp +++ b/clang/lib/Index/IndexingContext.cpp @@ -86,7 +86,6 @@ isa(D))) { return true; } - return handleDeclOccurrence(D, Loc, /*IsRef=*/true, Parent, Roles, Relations, RefE, RefD, DC); } @@ -264,7 +263,8 @@ isa(D) || isa(D) || isa(D) || - isa(D)) + isa(D) || + isa(D)) return true; return false; diff --git a/clang/lib/Index/USRGeneration.cpp b/clang/lib/Index/USRGeneration.cpp --- a/clang/lib/Index/USRGeneration.cpp +++ b/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. diff --git a/clang/tools/libclang/CXIndexDataConsumer.cpp b/clang/tools/libclang/CXIndexDataConsumer.cpp --- a/clang/tools/libclang/CXIndexDataConsumer.cpp +++ b/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;