diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp --- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp +++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp @@ -232,6 +232,15 @@ return true; } + bool VisitConceptDecl(ConceptDecl *CD) { + report(CD->getLocation(), CD); + return true; + } + + bool TraverseConceptReference(const ConceptReference &C) { + report(C.getConceptNameLoc(), C.getFoundDecl()); + return RecursiveASTVisitor::TraverseConceptReference(C); + } // Report a reference from explicit specializations to the specialized // template. Implicit ones are filtered out by RAV and explicit instantiations // are already traversed through typelocs. diff --git a/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp --- a/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp @@ -198,6 +198,45 @@ Pair(Code.point("4"), UnorderedElementsAre(MainFile)))); } +TEST_F(WalkUsedTest, Concepts) { + llvm::Annotations ConceptHdr(guard(R"cpp( + template + concept Foo = true; + )cpp")); + llvm::Annotations Code(R"cpp( + #include "concept.h" + + template + concept $Bar1^Bar = $Foo1^Foo && true; + + template<$Bar2^Bar T> requires $Bar3^Bar && $Foo2^Foo + void $1^func1() {} + + template<$Foo3^Foo T> + void $2^func2() requires $Bar4^Bar {} + )cpp"); + Inputs.Code = Code.code(); + Inputs.ExtraFiles["concept.h"] = ConceptHdr.code(); + Inputs.ExtraArgs.push_back("--std=c++20"); + TestAST AST(Inputs); + auto &SM = AST.sourceManager(); + const auto *ConceptHdrFile = SM.getFileManager().getFile("concept.h").get(); + auto MainFile = Header(SM.getFileEntryForID(SM.getMainFileID())); + + EXPECT_THAT( + offsetToProviders(AST), + UnorderedElementsAre( + Pair(Code.point("Foo1"), UnorderedElementsAre(ConceptHdrFile)), + Pair(Code.point("Foo2"), UnorderedElementsAre(ConceptHdrFile)), + Pair(Code.point("Foo3"), UnorderedElementsAre(ConceptHdrFile)), + Pair(Code.point("Bar1"), UnorderedElementsAre(MainFile)), + Pair(Code.point("Bar2"), UnorderedElementsAre(MainFile)), + Pair(Code.point("Bar3"), UnorderedElementsAre(MainFile)), + Pair(Code.point("Bar4"), UnorderedElementsAre(MainFile)), + Pair(Code.point("1"), UnorderedElementsAre(MainFile)), + Pair(Code.point("2"), UnorderedElementsAre(MainFile)))); +} + class AnalyzeTest : public testing::Test { protected: TestInputs Inputs; @@ -299,7 +338,7 @@ Results.Unused.push_back(Inc.atLine(4)); EXPECT_EQ(fixIncludes(Results, "d.cc", Code, format::getLLVMStyle()), -R"cpp(#include "d.h" + R"cpp(#include "d.h" #include "a.h" #include "aa.h" #include "ab.h" @@ -310,7 +349,7 @@ Results.Missing.push_back("\"d.h\""); Code = R"cpp(#include "a.h")cpp"; EXPECT_EQ(fixIncludes(Results, "d.cc", Code, format::getLLVMStyle()), -R"cpp(#include "d.h" + R"cpp(#include "d.h" #include "a.h")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 @@ -463,15 +463,13 @@ bool TraverseConceptTypeRequirement(concepts::TypeRequirement *R); bool TraverseConceptExprRequirement(concepts::ExprRequirement *R); bool TraverseConceptNestedRequirement(concepts::NestedRequirement *R); + bool TraverseConceptReference(const ConceptReference &C); bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue); private: // These are helper methods used by more than one Traverse* method. bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL); - /// Traverses the qualifier, name and template arguments of a concept - /// reference. - bool TraverseConceptReferenceHelper(const ConceptReference &C); // Traverses template parameter lists of either a DeclaratorDecl or TagDecl. template @@ -507,7 +505,7 @@ bool RecursiveASTVisitor::TraverseTypeConstraint( const TypeConstraint *C) { if (!getDerived().shouldVisitImplicitCode()) { - TRY_TO(TraverseConceptReferenceHelper(*C)); + TRY_TO(TraverseConceptReference(*C)); return true; } if (Expr *IDC = C->getImmediatelyDeclaredConstraint()) { @@ -517,7 +515,7 @@ // if we have an immediately-declared-constraint, otherwise // we'll end up visiting the concept and the arguments in // the TC twice. - TRY_TO(TraverseConceptReferenceHelper(*C)); + TRY_TO(TraverseConceptReference(*C)); } return true; } @@ -541,7 +539,7 @@ } template -bool RecursiveASTVisitor::TraverseConceptReferenceHelper( +bool RecursiveASTVisitor::TraverseConceptReference( const ConceptReference &C) { TRY_TO(TraverseNestedNameSpecifierLoc(C.getNestedNameSpecifierLoc())); TRY_TO(TraverseDeclarationNameInfo(C.getConceptNameInfo())); @@ -2904,7 +2902,7 @@ }) DEF_TRAVERSE_STMT(ConceptSpecializationExpr, - { TRY_TO(TraverseConceptReferenceHelper(*S)); }) + { TRY_TO(TraverseConceptReference(*S)); }) DEF_TRAVERSE_STMT(RequiresExpr, { TRY_TO(TraverseDecl(S->getBody()));