[clang] clang_EvalResult_getAsCXString impl (PR #134551)
Damian Andrei via cfe-commits
cfe-commits at lists.llvm.org
Sat Jun 28 10:03:16 PDT 2025
https://github.com/xTachyon updated https://github.com/llvm/llvm-project/pull/134551
>From 6d8d8955c1e3c706e3195e440f9435e5ae197f9f Mon Sep 17 00:00:00 2001
From: Andrei Damian <andreidaamian at gmail.com>
Date: Sun, 6 Apr 2025 19:55:59 +0300
Subject: [PATCH] clang_EvalResult_getAsCXString impl
---
clang/include/clang-c/CXString.h | 16 ++++++-
clang/include/clang-c/Index.h | 17 +++++--
clang/tools/libclang/CIndex.cpp | 44 +++++++----------
clang/tools/libclang/CXString.cpp | 57 +++++++++++++++++------
clang/tools/libclang/libclang.map | 2 +
clang/unittests/libclang/LibclangTest.cpp | 40 ++++++++++++++++
6 files changed, 129 insertions(+), 47 deletions(-)
diff --git a/clang/include/clang-c/CXString.h b/clang/include/clang-c/CXString.h
index 63dce4d140ce2..14d2eebbc8733 100644
--- a/clang/include/clang-c/CXString.h
+++ b/clang/include/clang-c/CXString.h
@@ -16,6 +16,7 @@
#include "clang-c/ExternC.h"
#include "clang-c/Platform.h"
+#include <stddef.h>
LLVM_CLANG_C_EXTERN_C_BEGIN
@@ -44,6 +45,11 @@ typedef struct {
unsigned Count;
} CXStringSet;
+typedef struct {
+ const char *string;
+ size_t length;
+} CStringInfo;
+
/**
* Retrieve the character data associated with the given string.
*
@@ -53,6 +59,15 @@ typedef struct {
*/
CINDEX_LINKAGE const char *clang_getCString(CXString string);
+/**
+ * Retrieve the character data associated with the given string and its length.
+ *
+ * The returned lenght might be bigger than strlen(.string) if the string
+ * contains nul bytes. This function has the same requirements and guarantees as
+ * clang_getCString.
+ */
+CINDEX_LINKAGE CStringInfo clang_getCStringInfo(CXString string);
+
/**
* Free the given string.
*/
@@ -70,4 +85,3 @@ CINDEX_LINKAGE void clang_disposeStringSet(CXStringSet *set);
LLVM_CLANG_C_EXTERN_C_END
#endif
-
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index c35311c886413..f86a1c25f6c92 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -6058,12 +6058,21 @@ clang_EvalResult_getAsUnsigned(CXEvalResult E);
CINDEX_LINKAGE double clang_EvalResult_getAsDouble(CXEvalResult E);
/**
- * Returns the evaluation result as a constant string if the
- * kind is other than Int or float. User must not free this pointer,
- * instead call clang_EvalResult_dispose on the CXEvalResult returned
- * by clang_Cursor_Evaluate.
+ * This function behaves the same as clang_EvalResult_getAsCXString, with 2
+ * exceptions:
+ * - the string literal will be truncated if a nul byte is found in the string.
+ * For this reason clang_EvalResult_getAsCXString is recommended.
+ * - the caller must not free this pointer; instead call
+ * clang_EvalResult_dispose on the CXEvalResult returned by
+ * clang_Cursor_Evaluate.
*/
CINDEX_LINKAGE const char *clang_EvalResult_getAsStr(CXEvalResult E);
+/**
+ * Returns the evaluation result as a CXString if the
+ * kind is other than Int or float. This might include zero bytes.
+ * The caller is responsible for freeing the CXString using clang_disposeString.
+ */
+CINDEX_LINKAGE CXString clang_EvalResult_getAsCXString(CXEvalResult E);
/**
* Disposes the created Eval memory.
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 9089984fa4a54..2d27a90b80544 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -4595,13 +4595,13 @@ struct ExprEvalResult {
unsigned long long unsignedVal;
long long intVal;
double floatVal;
- char *stringVal;
+ CXString stringVal;
} EvalData;
bool IsUnsignedInt;
~ExprEvalResult() {
if (EvalType != CXEval_UnExposed && EvalType != CXEval_Float &&
EvalType != CXEval_Int) {
- delete[] EvalData.stringVal;
+ clang_disposeString(EvalData.stringVal);
}
}
};
@@ -4657,7 +4657,15 @@ const char *clang_EvalResult_getAsStr(CXEvalResult E) {
if (!E) {
return nullptr;
}
- return ((ExprEvalResult *)E)->EvalData.stringVal;
+ return clang_getCString(((ExprEvalResult *)E)->EvalData.stringVal);
+}
+
+CXString clang_EvalResult_getAsCXString(CXEvalResult E) {
+ if (!E) {
+ return cxstring::createNull();
+ }
+ auto data = clang_getCStringInfo(((ExprEvalResult *)E)->EvalData.stringVal);
+ return cxstring::createDup(StringRef(data.string, data.length));
}
static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
@@ -4721,11 +4729,7 @@ static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
result->EvalType = CXEval_StrLiteral;
}
- std::string strRef(StrE->getString().str());
- result->EvalData.stringVal = new char[strRef.size() + 1];
- strncpy((char *)result->EvalData.stringVal, strRef.c_str(),
- strRef.size());
- result->EvalData.stringVal[strRef.size()] = '\0';
+ result->EvalData.stringVal = cxstring::createDup(StrE->getString());
return result.release();
}
} else if (expr->getStmtClass() == Stmt::ObjCStringLiteralClass ||
@@ -4742,10 +4746,7 @@ static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
result->EvalType = CXEval_StrLiteral;
}
- std::string strRef(StrE->getString().str());
- result->EvalData.stringVal = new char[strRef.size() + 1];
- strncpy((char *)result->EvalData.stringVal, strRef.c_str(), strRef.size());
- result->EvalData.stringVal[strRef.size()] = '\0';
+ result->EvalData.stringVal = cxstring::createDup(StrE->getString());
return result.release();
}
@@ -4759,13 +4760,8 @@ static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
callExpr = static_cast<CallExpr *>(CC->getSubExpr());
StringLiteral *S = getCFSTR_value(callExpr);
if (S) {
- std::string strLiteral(S->getString().str());
result->EvalType = CXEval_CFStr;
-
- result->EvalData.stringVal = new char[strLiteral.size() + 1];
- strncpy((char *)result->EvalData.stringVal, strLiteral.c_str(),
- strLiteral.size());
- result->EvalData.stringVal[strLiteral.size()] = '\0';
+ result->EvalData.stringVal = cxstring::createDup(S->getString());
return result.release();
}
}
@@ -4785,12 +4781,8 @@ static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
StringLiteral *S = getCFSTR_value(callExpr);
if (S) {
- std::string strLiteral(S->getString().str());
result->EvalType = CXEval_CFStr;
- result->EvalData.stringVal = new char[strLiteral.size() + 1];
- strncpy((char *)result->EvalData.stringVal, strLiteral.c_str(),
- strLiteral.size());
- result->EvalData.stringVal[strLiteral.size()] = '\0';
+ result->EvalData.stringVal = cxstring::createDup(S->getString());
return result.release();
}
}
@@ -4798,11 +4790,9 @@ static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
DeclRefExpr *D = static_cast<DeclRefExpr *>(expr);
ValueDecl *V = D->getDecl();
if (V->getKind() == Decl::Function) {
- std::string strName = V->getNameAsString();
result->EvalType = CXEval_Other;
- result->EvalData.stringVal = new char[strName.size() + 1];
- strncpy(result->EvalData.stringVal, strName.c_str(), strName.size());
- result->EvalData.stringVal[strName.size()] = '\0';
+ result->EvalData.stringVal =
+ cxstring::createDup(StringRef(V->getNameAsString()));
return result.release();
}
}
diff --git a/clang/tools/libclang/CXString.cpp b/clang/tools/libclang/CXString.cpp
index aaa8f8eeb67a1..8ad981f5533dc 100644
--- a/clang/tools/libclang/CXString.cpp
+++ b/clang/tools/libclang/CXString.cpp
@@ -25,14 +25,19 @@ enum CXStringFlag {
/// CXString contains a 'const char *' that it doesn't own.
CXS_Unmanaged,
- /// CXString contains a 'const char *' that it allocated with malloc().
- CXS_Malloc,
+ /// CXString contains a 'CStringImpl' that it allocated with malloc().
+ CXS_MallocWithSize,
/// CXString contains a CXStringBuf that needs to be returned to the
/// CXStringPool.
CXS_StringBuf
};
+struct CStringImpl {
+ size_t length;
+ char buffer[sizeof(length)];
+};
+
namespace clang {
namespace cxstring {
@@ -71,10 +76,7 @@ CXString createDup(const char *String) {
if (String[0] == '\0')
return createEmpty();
- CXString Str;
- Str.data = strdup(String);
- Str.private_flags = CXS_Malloc;
- return Str;
+ return createDup(StringRef(String));
}
CXString createRef(StringRef String) {
@@ -91,12 +93,18 @@ CXString createRef(StringRef String) {
}
CXString createDup(StringRef String) {
+ auto toAllocate =
+ sizeof(size_t) + std::max(sizeof(size_t), String.size() + 1);
+ assert(toAllocate >= sizeof(CStringImpl));
+ auto ptr = static_cast<CStringImpl *>(llvm::safe_malloc(toAllocate));
+
+ ptr->length = String.size();
+ memcpy(ptr->buffer, String.data(), String.size());
+ ptr->buffer[String.size()] = 0;
+
CXString Result;
- char *Spelling = static_cast<char *>(llvm::safe_malloc(String.size() + 1));
- memmove(Spelling, String.data(), String.size());
- Spelling[String.size()] = 0;
- Result.data = Spelling;
- Result.private_flags = (unsigned) CXS_Malloc;
+ Result.data = ptr;
+ Result.private_flags = (unsigned)CXS_MallocWithSize;
return Result;
}
@@ -164,19 +172,38 @@ const char *clang_getCString(CXString string) {
return static_cast<const char *>(string.data);
}
+CStringInfo clang_getCStringInfo(CXString string) {
+ switch ((CXStringFlag)string.private_flags) {
+ case CXS_Unmanaged: {
+ auto ptr = static_cast<const char *>(string.data);
+ return {ptr, strlen(ptr)};
+ }
+ case CXS_MallocWithSize: {
+ auto ptr = static_cast<const CStringImpl *>(string.data);
+ return {ptr->buffer, ptr->length};
+ }
+ case CXS_StringBuf: {
+ auto ptr = static_cast<const cxstring::CXStringBuf *>(string.data);
+ return {ptr->Data.data(), ptr->Data.size()};
+ }
+ }
+ llvm_unreachable("Invalid CXString::private_flags");
+}
+
void clang_disposeString(CXString string) {
switch ((CXStringFlag) string.private_flags) {
case CXS_Unmanaged:
- break;
- case CXS_Malloc:
+ return;
+ case CXS_MallocWithSize:
if (string.data)
free(const_cast<void *>(string.data));
- break;
+ return;
case CXS_StringBuf:
static_cast<cxstring::CXStringBuf *>(
const_cast<void *>(string.data))->dispose();
- break;
+ return;
}
+ llvm_unreachable("Invalid CXString::private_flags");
}
void clang_disposeStringSet(CXStringSet *set) {
diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map
index d140a71e771a0..0f7b7f1ad8979 100644
--- a/clang/tools/libclang/libclang.map
+++ b/clang/tools/libclang/libclang.map
@@ -450,6 +450,8 @@ LLVM_21 {
clang_Cursor_getGCCAssemblyNumClobbers;
clang_Cursor_getGCCAssemblyClobber;
clang_Cursor_isGCCAssemblyVolatile;
+ clang_getCStringInfo;
+ clang_EvalResult_getAsCXString;
};
# Example of how to add a new symbol version entry. If you do add a new symbol
diff --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp
index b2a87d240e56e..1ae56bbdc546b 100644
--- a/clang/unittests/libclang/LibclangTest.cpp
+++ b/clang/unittests/libclang/LibclangTest.cpp
@@ -623,6 +623,46 @@ TEST_F(LibclangParseTest, EvaluateChildExpression) {
nullptr);
}
+TEST_F(LibclangParseTest, StringLiteralWithZeros) {
+ const char testSource[] = R"cpp(
+const char str[] = "pika\0chu";
+)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);
+
+ int nodes = 0;
+
+ Traverse([&nodes](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
+ if (cursor.kind == CXCursor_StringLiteral) {
+ CXEvalResult RE = clang_Cursor_Evaluate(cursor);
+ EXPECT_NE(RE, nullptr);
+ EXPECT_EQ(clang_EvalResult_getKind(RE), CXEval_StrLiteral);
+
+ const char expected[] = "pika\0chu";
+ size_t expected_size = sizeof(expected) - 1;
+
+ auto lit = clang_EvalResult_getAsCXString(RE);
+ auto str = clang_getCStringInfo(lit);
+
+ EXPECT_TRUE(str.length == expected_size &&
+ memcmp(str.string, expected, str.length) == 0);
+
+ clang_disposeString(lit);
+ clang_EvalResult_dispose(RE);
+
+ nodes++;
+ return CXChildVisit_Continue;
+ }
+ return CXChildVisit_Recurse;
+ });
+
+ EXPECT_EQ(nodes, 1);
+}
+
class LibclangReparseTest : public LibclangParseTest {
public:
void DisplayDiagnostics() {
More information about the cfe-commits
mailing list