[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
This commit is contained in:
Kai Stierand 2023-07-25 10:36:53 +02:00 committed by Corentin Jabot
parent ca9a3354d0
commit 86da763ab6
3 changed files with 85 additions and 7 deletions

View 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;

View 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;

View File

@ -88,13 +88,17 @@ public:
});
}
template <typename F>
void Traverse(const F &TraversalFunctor) {
CXCursor TuCursor = clang_getTranslationUnitCursor(ClangTU);
void Traverse(const CXCursor &cursor, const F &TraversalFunctor) {
std::reference_wrapper<const F> FunctorRef = std::cref(TraversalFunctor);
clang_visitChildren(TuCursor,
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);