[clang] clang_EvalResult_getAsCXString impl (PR #134551)
Damian Andrei via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 7 10:30:50 PDT 2025
https://github.com/xTachyon updated https://github.com/llvm/llvm-project/pull/134551
>From 88a7517918ff8f6a5521527e9e1a141af09035c5 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 1/3] clang_EvalResult_getAsCXString impl
---
clang/include/clang-c/CXString.h | 3 ++
clang/include/clang-c/Index.h | 15 ++++++--
clang/tools/libclang/CIndex.cpp | 46 ++++++++++-------------
clang/tools/libclang/CXString.cpp | 22 ++++++++---
clang/tools/libclang/libclang.map | 6 +++
clang/unittests/libclang/LibclangTest.cpp | 41 ++++++++++++++++++++
6 files changed, 96 insertions(+), 37 deletions(-)
diff --git a/clang/include/clang-c/CXString.h b/clang/include/clang-c/CXString.h
index 63dce4d140ce2..a0ad830230338 100644
--- a/clang/include/clang-c/CXString.h
+++ b/clang/include/clang-c/CXString.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_C_CXSTRING_H
#define LLVM_CLANG_C_CXSTRING_H
+#include <stddef.h>
#include "clang-c/ExternC.h"
#include "clang-c/Platform.h"
@@ -36,6 +37,7 @@ LLVM_CLANG_C_EXTERN_C_BEGIN
*/
typedef struct {
const void *data;
+ size_t length;
unsigned private_flags;
} CXString;
@@ -52,6 +54,7 @@ typedef struct {
* to `std::string::c_str()`.
*/
CINDEX_LINKAGE const char *clang_getCString(CXString string);
+CINDEX_LINKAGE const char *clang_getCString2(CXString string, size_t *length);
/**
* Free the given string.
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 38e2417dcd181..4ef41d56e43ae 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -5918,12 +5918,19 @@ 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.
+ * - User 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 constant string if the
+ * kind is other than Int or float. This might include zero bytes.
+ */
+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 c8db6c92bb4d4..672e791ad3455 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -4589,13 +4589,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);
}
}
};
@@ -4651,7 +4651,17 @@ 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();
+ }
+ size_t length;
+ auto data =
+ clang_getCString2(((ExprEvalResult *)E)->EvalData.stringVal, &length);
+ return cxstring::createDup(StringRef(data, length));
}
static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
@@ -4716,11 +4726,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 ||
@@ -4737,10 +4743,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();
}
@@ -4754,13 +4757,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();
}
}
@@ -4780,12 +4778,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();
}
}
@@ -4793,11 +4787,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..7cc3e6681c542 100644
--- a/clang/tools/libclang/CXString.cpp
+++ b/clang/tools/libclang/CXString.cpp
@@ -43,6 +43,7 @@ namespace cxstring {
CXString createEmpty() {
CXString Str;
Str.data = "";
+ Str.length = 0;
Str.private_flags = CXS_Unmanaged;
return Str;
}
@@ -50,6 +51,7 @@ CXString createEmpty() {
CXString createNull() {
CXString Str;
Str.data = nullptr;
+ Str.length = 0;
Str.private_flags = CXS_Unmanaged;
return Str;
}
@@ -60,6 +62,7 @@ CXString createRef(const char *String) {
CXString Str;
Str.data = String;
+ Str.length = -1;
Str.private_flags = CXS_Unmanaged;
return Str;
}
@@ -71,10 +74,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,11 +91,13 @@ CXString createRef(StringRef String) {
}
CXString createDup(StringRef String) {
- CXString Result;
char *Spelling = static_cast<char *>(llvm::safe_malloc(String.size() + 1));
- memmove(Spelling, String.data(), String.size());
+ memcpy(Spelling, String.data(), String.size());
Spelling[String.size()] = 0;
+
+ CXString Result;
Result.data = Spelling;
+ Result.length = String.size();
Result.private_flags = (unsigned) CXS_Malloc;
return Result;
}
@@ -103,6 +105,7 @@ CXString createDup(StringRef String) {
CXString createCXString(CXStringBuf *buf) {
CXString Str;
Str.data = buf;
+ Str.length = -1;
Str.private_flags = (unsigned) CXS_StringBuf;
return Str;
}
@@ -164,6 +167,13 @@ const char *clang_getCString(CXString string) {
return static_cast<const char *>(string.data);
}
+const char *clang_getCString2(CXString string, size_t *length) {
+ auto ret = clang_getCString(string);
+ *length =
+ string.length == static_cast<size_t>(-1) ? strlen(ret) : string.length;
+ return ret;
+}
+
void clang_disposeString(CXString string) {
switch ((CXStringFlag) string.private_flags) {
case CXS_Unmanaged:
diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map
index 07471ca42c97e..85ac50bb59c62 100644
--- a/clang/tools/libclang/libclang.map
+++ b/clang/tools/libclang/libclang.map
@@ -438,6 +438,12 @@ LLVM_20 {
clang_visitCXXMethods;
};
+LLVM_21 {
+ global:
+ clang_getCString2;
+ clang_EvalResult_getAsCXString;
+};
+
# Example of how to add a new symbol version entry. If you do add a new symbol
# version, please update the example to depend on the version you added.
# LLVM_X {
diff --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp
index 6de4d02bf74f4..c242d194b2c04 100644
--- a/clang/unittests/libclang/LibclangTest.cpp
+++ b/clang/unittests/libclang/LibclangTest.cpp
@@ -623,6 +623,47 @@ 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);
+ size_t length;
+ auto str = clang_getCString2(lit, &length);
+
+ EXPECT_TRUE(length == expected_size &&
+ memcmp(str, expected, 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() {
>From 2854d78109e668e3e130f46329551df16e75d7d6 Mon Sep 17 00:00:00 2001
From: Andrei Damian <andreidaamian at gmail.com>
Date: Mon, 7 Apr 2025 18:21:48 +0300
Subject: [PATCH 2/3] clang_EvalResult_getAsCXString address comments
---
clang/include/clang-c/CXString.h | 7 ++++++-
clang/include/clang-c/Index.h | 8 +++++---
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/clang/include/clang-c/CXString.h b/clang/include/clang-c/CXString.h
index a0ad830230338..b97d19df78944 100644
--- a/clang/include/clang-c/CXString.h
+++ b/clang/include/clang-c/CXString.h
@@ -14,9 +14,9 @@
#ifndef LLVM_CLANG_C_CXSTRING_H
#define LLVM_CLANG_C_CXSTRING_H
-#include <stddef.h>
#include "clang-c/ExternC.h"
#include "clang-c/Platform.h"
+#include <stddef.h>
LLVM_CLANG_C_EXTERN_C_BEGIN
@@ -54,6 +54,11 @@ typedef struct {
* to `std::string::c_str()`.
*/
CINDEX_LINKAGE const char *clang_getCString(CXString string);
+/**
+ * This function behaves the same as clang_getCString, except that it also
+ * returns the size through the length parameter. The length parameter should be
+ * non-NULL.
+ */
CINDEX_LINKAGE const char *clang_getCString2(CXString string, size_t *length);
/**
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 4ef41d56e43ae..08fe0c5f65d50 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -5922,13 +5922,15 @@ CINDEX_LINKAGE double clang_EvalResult_getAsDouble(CXEvalResult E);
* exceptions:
* - the string literal will be truncated if a nul byte is found in the string.
* For this reason clang_EvalResult_getAsCXString is recommended.
- * - User must not free this pointer, instead call clang_EvalResult_dispose on
- * the CXEvalResult returned by clang_Cursor_Evaluate.
+ * - 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 constant string if the
+ * 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);
>From 27327a408ce9e8c08ce1fb991a554131d7e8fdcc Mon Sep 17 00:00:00 2001
From: Andrei Damian <andreidaamian at gmail.com>
Date: Mon, 7 Apr 2025 20:27:58 +0300
Subject: [PATCH 3/3] rename function to clang_getCStringAndLength
---
clang/include/clang-c/CXString.h | 3 ++-
clang/tools/libclang/CIndex.cpp | 4 ++--
clang/tools/libclang/CXString.cpp | 2 +-
clang/tools/libclang/libclang.map | 2 +-
clang/unittests/libclang/LibclangTest.cpp | 2 +-
5 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang-c/CXString.h b/clang/include/clang-c/CXString.h
index b97d19df78944..b64a93c6d1dc8 100644
--- a/clang/include/clang-c/CXString.h
+++ b/clang/include/clang-c/CXString.h
@@ -59,7 +59,8 @@ CINDEX_LINKAGE const char *clang_getCString(CXString string);
* returns the size through the length parameter. The length parameter should be
* non-NULL.
*/
-CINDEX_LINKAGE const char *clang_getCString2(CXString string, size_t *length);
+CINDEX_LINKAGE const char *clang_getCStringAndLength(CXString string,
+ size_t *length);
/**
* Free the given string.
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 672e791ad3455..f45735737b0a5 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -4659,8 +4659,8 @@ CXString clang_EvalResult_getAsCXString(CXEvalResult E) {
return cxstring::createNull();
}
size_t length;
- auto data =
- clang_getCString2(((ExprEvalResult *)E)->EvalData.stringVal, &length);
+ auto data = clang_getCStringAndLength(
+ ((ExprEvalResult *)E)->EvalData.stringVal, &length);
return cxstring::createDup(StringRef(data, length));
}
diff --git a/clang/tools/libclang/CXString.cpp b/clang/tools/libclang/CXString.cpp
index 7cc3e6681c542..6c0efc57639fd 100644
--- a/clang/tools/libclang/CXString.cpp
+++ b/clang/tools/libclang/CXString.cpp
@@ -167,7 +167,7 @@ const char *clang_getCString(CXString string) {
return static_cast<const char *>(string.data);
}
-const char *clang_getCString2(CXString string, size_t *length) {
+const char *clang_getCStringAndLength(CXString string, size_t *length) {
auto ret = clang_getCString(string);
*length =
string.length == static_cast<size_t>(-1) ? strlen(ret) : string.length;
diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map
index 85ac50bb59c62..af933b927cdd5 100644
--- a/clang/tools/libclang/libclang.map
+++ b/clang/tools/libclang/libclang.map
@@ -440,7 +440,7 @@ LLVM_20 {
LLVM_21 {
global:
- clang_getCString2;
+ clang_getCStringAndLength;
clang_EvalResult_getAsCXString;
};
diff --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp
index c242d194b2c04..64ed557a7aba8 100644
--- a/clang/unittests/libclang/LibclangTest.cpp
+++ b/clang/unittests/libclang/LibclangTest.cpp
@@ -647,7 +647,7 @@ const char str[] = "pika\0chu";
auto lit = clang_EvalResult_getAsCXString(RE);
size_t length;
- auto str = clang_getCString2(lit, &length);
+ auto str = clang_getCStringAndLength(lit, &length);
EXPECT_TRUE(length == expected_size &&
memcmp(str, expected, length) == 0);
More information about the cfe-commits
mailing list