Skip to content

clang+libclang indexing support for C++20 concepts #4734

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/CodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
case SK::TemplateTypeParm:
case SK::TemplateTemplateParm:
return CompletionItemKind::TypeParameter;
case SK::Concept:
return CompletionItemKind::Interface;
}
llvm_unreachable("Unhandled clang::index::SymbolKind.");
}
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/Protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) {
case index::SymbolKind::TemplateTemplateParm:
case index::SymbolKind::TemplateTypeParm:
return SymbolKind::TypeParameter;
case index::SymbolKind::Concept:
return SymbolKind::Interface;
}
llvm_unreachable("invalid symbol kind");
}
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/Quality.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ categorize(const index::SymbolInfo &D) {
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:
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/index/SymbolCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ bool shouldCollectIncludePath(index::SymbolKind Kind) {
case SK::Function:
case SK::Variable:
case SK::EnumConstant:
case SK::Concept:
return true;
default:
return false;
Expand Down
32 changes: 32 additions & 0 deletions clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3308,6 +3308,38 @@ TEST(CompletionTest, CommentParamName) {
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 = $other^A<T> && $other^sizeof(T) % 2 == 0 || $other^A<T> && sizeof(T) == 1;

$other^A<T> auto i = 19;
)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("other")) {
EXPECT_THAT(completions(TU, P, Syms).Completions,
AllOf(Contains(named("A")), Contains(named("same_as"))))
<< "Completing 'requires' expression at position " << P;
}
}

} // namespace
} // namespace clangd
} // namespace clang
12 changes: 12 additions & 0 deletions clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ MATCHER_P(HasName, Name, "") { return arg.Name == Name; }
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;
}
Expand Down Expand Up @@ -1849,6 +1850,17 @@ TEST_F(SymbolCollectorTest, NoCrashOnObjCMethodCStyleParam) {
UnorderedElementsAre(QName("Foo"), QName("Foo::fun:")));
}

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
4 changes: 4 additions & 0 deletions clang-tools-extra/clangd/unittests/TestIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ Symbol ns(llvm::StringRef Name) {
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)
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/unittests/TestIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Symbol cls(llvm::StringRef Name);
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);
Expand Down
34 changes: 34 additions & 0 deletions clang-tools-extra/clangd/unittests/XRefsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1771,6 +1771,8 @@ TEST(FindImplementations, CaptureDefintion) {
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<Matcher<ReferencesResult::Reference>> ExpectedLocations;
for (const auto &R : T.ranges())
Expand Down Expand Up @@ -1985,6 +1987,38 @@ TEST(FindReferences, WithinAST) {
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 = 'c';
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]]<char>);
)cpp";
checkFindRefs(Code);
}

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);
}

TEST(FindReferences, IncludeOverrides) {
llvm::StringRef Test =
R"cpp(
Expand Down
22 changes: 19 additions & 3 deletions clang/include/clang-c/Index.h
Original file line number Diff line number Diff line change
Expand Up @@ -2189,7 +2189,17 @@ enum CXCursorKind {
*/
CXCursor_CXXAddrspaceCastExpr = 152,

CXCursor_LastExpr = CXCursor_CXXAddrspaceCastExpr,
/**
* Expression that references a C++20 concept.
*/
CXCursor_ConceptSpecializationExpr = 153,

/**
* Expression that references a C++20 concept.
*/
CXCursor_RequiresExpr = 154,

CXCursor_LastExpr = CXCursor_RequiresExpr,

/* Statements */
CXCursor_FirstStmt = 200,
Expand Down Expand Up @@ -2680,8 +2690,13 @@ enum CXCursorKind {
* a friend declaration.
*/
CXCursor_FriendDecl = 603,
/**
* a concept declaration.
*/
CXCursor_ConceptDecl = 604,

CXCursor_FirstExtraDecl = CXCursor_ModuleImportDecl,
CXCursor_LastExtraDecl = CXCursor_FriendDecl,
CXCursor_LastExtraDecl = CXCursor_ConceptDecl,

/**
* A code completion overload candidate.
Expand Down Expand Up @@ -6297,7 +6312,8 @@ typedef enum {
CXIdxEntity_CXXDestructor = 23,
CXIdxEntity_CXXConversionFunction = 24,
CXIdxEntity_CXXTypeAlias = 25,
CXIdxEntity_CXXInterface = 26
CXIdxEntity_CXXInterface = 26,
CXIdxEntity_CXXConcept = 27

} CXIdxEntityKind;

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/ExprConcepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ class ConceptSpecializationExpr final : public Expr, public ConceptReference,
}

SourceLocation getBeginLoc() const LLVM_READONLY {
if (auto QualifierLoc = getNestedNameSpecifierLoc())
return QualifierLoc.getBeginLoc();
return ConceptName.getBeginLoc();
}

Expand Down
7 changes: 4 additions & 3 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2795,9 +2795,10 @@ DEF_TRAVERSE_STMT(RequiresExpr, {
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())
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Index/IndexSymbol.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ enum class SymbolKind : uint8_t {
TemplateTypeParm,
TemplateTemplateParm,
NonTypeTemplateParm,

Concept, /// C++20 concept.
};

enum class SymbolLanguage : uint8_t {
Expand Down
25 changes: 22 additions & 3 deletions clang/lib/Index/IndexBody.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -455,10 +459,10 @@ class BodyIndexer : public RecursiveASTVisitor<BodyIndexer> {
}

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;
Expand All @@ -472,6 +476,21 @@ class BodyIndexer : public RecursiveASTVisitor<BodyIndexer> {
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
Expand Down
59 changes: 40 additions & 19 deletions clang/lib/Index/IndexDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -129,6 +132,8 @@ class IndexingDeclVisitor : public ConstDeclVisitor<IndexingDeclVisitor, bool> {
}
}
}
if (auto *C = D->getTrailingRequiresClause())
IndexCtx.indexBody(C, Parent);
}

bool handleObjCMethod(const ObjCMethodDecl *D,
Expand Down Expand Up @@ -688,36 +693,52 @@ class IndexingDeclVisitor : public ConstDeclVisitor<IndexingDeclVisitor, bool> {
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
Expand Down
Loading