diff options
author | Kai Stierand <kai.stierand@swplusplus.de> | 2023-07-25 10:36:53 +0200 |
---|---|---|
committer | Corentin Jabot <corentinjabot@gmail.com> | 2023-07-25 10:53:14 +0200 |
commit | 86da763ab6ed19c58349d3f6f62d4bb52becab2c (patch) | |
tree | 2cf7e8005ade248aa6485360b82bc43b0807af48 | |
parent | ca9a3354d04b15366088d7831b40f891e3d77b95 (diff) | |
download | llvm-86da763ab6ed19c58349d3f6f62d4bb52becab2c.zip llvm-86da763ab6ed19c58349d3f6f62d4bb52becab2c.tar.gz llvm-86da763ab6ed19c58349d3f6f62d4bb52becab2c.tar.bz2 |
[Clang] Fix crash in CIndex, when visiting a static_assert without message
After implementation of "[Clang] Implement P2741R3 - user-generated static_assert messages" (47ccfd7a89e2a9a747a7114db18db1376324799c) the c indexer crashes when handling a `static_assert` w/o any message.
This is caused by using `dyn_cast` to get the literal string, which isn't working on `nullptr`.
Reviewed By: cor3ntin
Differential Revision: https://reviews.llvm.org/D156053
-rw-r--r-- | clang/tools/libclang/CIndex.cpp | 2 | ||||
-rw-r--r-- | clang/unittests/libclang/LibclangTest.cpp | 74 | ||||
-rw-r--r-- | clang/unittests/libclang/TestUtils.h | 16 |
3 files changed, 85 insertions, 7 deletions
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 120ad4e..1bdc0bf 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1294,7 +1294,7 @@ bool CursorVisitor::VisitUnresolvedUsingTypenameDecl( bool CursorVisitor::VisitStaticAssertDecl(StaticAssertDecl *D) { if (Visit(MakeCXCursor(D->getAssertExpr(), StmtParent, TU, RegionOfInterest))) return true; - if (auto *Message = dyn_cast<StringLiteral>(D->getMessage())) + if (auto *Message = D->getMessage()) if (Visit(MakeCXCursor(Message, StmtParent, TU, RegionOfInterest))) return true; return false; diff --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp index f85a72b..b1f6534 100644 --- a/clang/unittests/libclang/LibclangTest.cpp +++ b/clang/unittests/libclang/LibclangTest.cpp @@ -1172,6 +1172,80 @@ TEST_F(LibclangParseTest, UnaryOperator) { }); } +TEST_F(LibclangParseTest, VisitStaticAssertDecl_noMessage) { + const char testSource[] = R"cpp(static_assert(true))cpp"; + std::string fileName = "main.cpp"; + WriteFile(fileName, testSource); + const char *Args[] = {"-xc++"}; + ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args, 1, + nullptr, 0, TUFlags); + + std::optional<CXCursor> staticAssertCsr; + Traverse([&](CXCursor cursor, CXCursor parent) -> CXChildVisitResult { + if (cursor.kind == CXCursor_StaticAssert) { + staticAssertCsr.emplace(cursor); + return CXChildVisit_Break; + } + return CXChildVisit_Recurse; + }); + ASSERT_TRUE(staticAssertCsr.has_value()); + Traverse(*staticAssertCsr, [](CXCursor cursor, CXCursor parent) { + EXPECT_EQ(cursor.kind, CXCursor_CXXBoolLiteralExpr); + return CXChildVisit_Break; + }); + EXPECT_EQ(fromCXString(clang_getCursorSpelling(*staticAssertCsr)), ""); +} + +TEST_F(LibclangParseTest, VisitStaticAssertDecl_exprMessage) { + const char testSource[] = R"cpp( +template <unsigned s> +constexpr unsigned size(const char (&)[s]) +{ + return s - 1; +} + +struct Message { + static constexpr char message[] = "Hello World!"; + constexpr const char* data() const { return message;} + constexpr unsigned size() const + { + return ::size(message); + } +}; +Message message; +static_assert(true, message); +)cpp"; + std::string fileName = "main.cpp"; + WriteFile(fileName, testSource); + const char *Args[] = {"-xc++", "-std=c++26"}; + ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args, + std::size(Args), nullptr, 0, TUFlags); + ASSERT_EQ(clang_getNumDiagnostics(ClangTU), 0); + std::optional<CXCursor> staticAssertCsr; + Traverse([&](CXCursor cursor, CXCursor parent) -> CXChildVisitResult { + if (cursor.kind == CXCursor_StaticAssert) { + staticAssertCsr.emplace(cursor); + } + return CXChildVisit_Continue; + }); + ASSERT_TRUE(staticAssertCsr.has_value()); + size_t argCnt = 0; + Traverse(*staticAssertCsr, [&argCnt](CXCursor cursor, CXCursor parent) { + switch (argCnt) { + case 0: + EXPECT_EQ(cursor.kind, CXCursor_CXXBoolLiteralExpr); + break; + case 1: + EXPECT_EQ(cursor.kind, CXCursor_DeclRefExpr); + break; + } + ++argCnt; + return CXChildVisit_Continue; + }); + ASSERT_EQ(argCnt, 2); + EXPECT_EQ(fromCXString(clang_getCursorSpelling(*staticAssertCsr)), ""); +} + class LibclangRewriteTest : public LibclangParseTest { public: CXRewriter Rew = nullptr; diff --git a/clang/unittests/libclang/TestUtils.h b/clang/unittests/libclang/TestUtils.h index c78351d..013aad1 100644 --- a/clang/unittests/libclang/TestUtils.h +++ b/clang/unittests/libclang/TestUtils.h @@ -87,14 +87,18 @@ public: it.first->second->size() // length }); } - template<typename F> - void Traverse(const F &TraversalFunctor) { - CXCursor TuCursor = clang_getTranslationUnitCursor(ClangTU); + template <typename F> + void Traverse(const CXCursor &cursor, const F &TraversalFunctor) { std::reference_wrapper<const F> FunctorRef = std::cref(TraversalFunctor); - clang_visitChildren(TuCursor, - &TraverseStateless<std::reference_wrapper<const F>>, - &FunctorRef); + clang_visitChildren(cursor, + &TraverseStateless<std::reference_wrapper<const F>>, + &FunctorRef); } + + template <typename F> void Traverse(const F &TraversalFunctor) { + Traverse(clang_getTranslationUnitCursor(ClangTU), TraversalFunctor); + } + static std::string fromCXString(CXString cx_string) { std::string string{clang_getCString(cx_string)}; clang_disposeString(cx_string); |