[Clang] Fix crash in CIndex, when visiting a static_assert without message
authorKai Stierand <kai.stierand@swplusplus.de>
Tue, 25 Jul 2023 08:36:53 +0000 (10:36 +0200)
committerCorentin Jabot <corentinjabot@gmail.com>
Tue, 25 Jul 2023 08:53:14 +0000 (10:53 +0200)
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

clang/tools/libclang/CIndex.cpp
clang/unittests/libclang/LibclangTest.cpp
clang/unittests/libclang/TestUtils.h

index 120ad4ea39e1a9a7c45c1f942793119221562c82..1bdc0bf742a8ce51ad652651a54df658cb8b451b 100644 (file)
@@ -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;
index f85a72b1c2f702c8068e239226119dd21ab613a6..b1f653441d0efeac7f7d577c29fd5cab2458ceb5 100644 (file)
@@ -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;
index c78351d4afd200de69507893d1a0f38c7fd11f44..013aad17358211fe0f36731356ea8e3284316aa8 100644 (file)
@@ -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);