BartmanAbyss created this revision. BartmanAbyss added reviewers: klimek, rsmith, akyrtzi. BartmanAbyss added projects: clang-c, clang. Herald added a subscriber: arphaman. BartmanAbyss requested review of this revision. Herald added a subscriber: cfe-commits.
This allows evaluating array initializer such as int array[4]{1, 2, 3, 4}; const char* str[2]{ "hello", "world" }; Adds `CXEval_Array` to the `CXEvalResultKind` enum, `clang_EvalResult_getArraySize()`, `clang_EvalResult_getArrayElt` Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D120061 Files: clang/include/clang-c/Index.h clang/test/Index/evaluate-cursor.cpp clang/tools/c-index-test/c-index-test.c clang/tools/libclang/CIndex.cpp
Index: clang/tools/libclang/CIndex.cpp =================================================================== --- clang/tools/libclang/CIndex.cpp +++ clang/tools/libclang/CIndex.cpp @@ -3900,10 +3900,16 @@ long long intVal; double floatVal; char *stringVal; + struct { + ExprEvalResult *elts; + unsigned size; + } arrayVal; } EvalData; bool IsUnsignedInt; ~ExprEvalResult() { - if (EvalType != CXEval_UnExposed && EvalType != CXEval_Float && + if (EvalType == CXEval_Array) { + delete[] EvalData.arrayVal.elts; + } else if (EvalType != CXEval_UnExposed && EvalType != CXEval_Float && EvalType != CXEval_Int) { delete[] EvalData.stringVal; } @@ -3964,51 +3970,75 @@ return ((ExprEvalResult *)E)->EvalData.stringVal; } -static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) { - Expr::EvalResult ER; - ASTContext &ctx = getCursorContext(C); - if (!expr) - return nullptr; +unsigned clang_EvalResult_getArraySize(CXEvalResult E) { + if (!E || ((ExprEvalResult *)E)->EvalType != CXEval_Array) { + return 0; + } + return ((ExprEvalResult *)E)->EvalData.arrayVal.size; +} - expr = expr->IgnoreParens(); - if (expr->isValueDependent()) - return nullptr; - if (!expr->EvaluateAsRValue(ER, ctx)) - return nullptr; +CXEvalResult clang_EvalResult_getArrayElt(CXEvalResult E, unsigned index) { + if (E && ((ExprEvalResult *)E)->EvalType == CXEval_Array && + index < ((ExprEvalResult *)E)->EvalData.arrayVal.size) { + return &((ExprEvalResult *)E)->EvalData.arrayVal.elts[index]; + } + return nullptr; +} - QualType rettype; - CallExpr *callExpr; - auto result = std::make_unique<ExprEvalResult>(); - result->EvalType = CXEval_UnExposed; - result->IsUnsignedInt = false; +static bool evaluateVal(const APValue &val, ExprEvalResult &result) { + result.EvalType = CXEval_UnExposed; + result.IsUnsignedInt = false; + + if (val.isArray()) { + result.EvalType = CXEval_Array; + result.EvalData.arrayVal.size = val.getArrayInitializedElts(); + result.EvalData.arrayVal.elts = + new ExprEvalResult[result.EvalData.arrayVal.size]; + bool ret = true; + for (unsigned i = 0; i < result.EvalData.arrayVal.size; i++) { + if (!evaluateVal(val.getArrayInitializedElt(i), + result.EvalData.arrayVal.elts[i])) { + ret = false; + } + } - if (ER.Val.isInt()) { - result->EvalType = CXEval_Int; + return ret; + } - auto &val = ER.Val.getInt(); - if (val.isUnsigned()) { - result->IsUnsignedInt = true; - result->EvalData.unsignedVal = val.getZExtValue(); + if (val.isInt()) { + result.EvalType = CXEval_Int; + + auto &ival = val.getInt(); + if (ival.isUnsigned()) { + result.IsUnsignedInt = true; + result.EvalData.unsignedVal = ival.getZExtValue(); } else { - result->EvalData.intVal = val.getExtValue(); + result.EvalData.intVal = ival.getExtValue(); } - return result.release(); + return true; } - if (ER.Val.isFloat()) { + if (val.isFloat()) { llvm::SmallVector<char, 100> Buffer; - ER.Val.getFloat().toString(Buffer); + val.getFloat().toString(Buffer); std::string floatStr(Buffer.data(), Buffer.size()); - result->EvalType = CXEval_Float; + result.EvalType = CXEval_Float; bool ignored; - llvm::APFloat apFloat = ER.Val.getFloat(); + llvm::APFloat apFloat = val.getFloat(); apFloat.convert(llvm::APFloat::IEEEdouble(), llvm::APFloat::rmNearestTiesToEven, &ignored); - result->EvalData.floatVal = apFloat.convertToDouble(); - return result.release(); + result.EvalData.floatVal = apFloat.convertToDouble(); + return true; } + return false; +} + +static bool evaluateStringExpr(Expr *expr, ASTContext &ctx, ExprEvalResult &result) { + QualType rettype; + CallExpr *callExpr; + if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) { const ImplicitCastExpr *I = dyn_cast<ImplicitCastExpr>(expr); auto *subExpr = I->getSubExprAsWritten(); @@ -4020,18 +4050,18 @@ if (ObjCExpr) { StrE = ObjCExpr->getString(); - result->EvalType = CXEval_ObjCStrLiteral; + result.EvalType = CXEval_ObjCStrLiteral; } else { StrE = cast<StringLiteral>(I->getSubExprAsWritten()); - result->EvalType = CXEval_StrLiteral; + 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(), + result.EvalData.stringVal = new char[strRef.size() + 1]; + strncpy((char *)result.EvalData.stringVal, strRef.c_str(), strRef.size()); - result->EvalData.stringVal[strRef.size()] = '\0'; - return result.release(); + result.EvalData.stringVal[strRef.size()] = '\0'; + return true; } } else if (expr->getStmtClass() == Stmt::ObjCStringLiteralClass || expr->getStmtClass() == Stmt::StringLiteralClass) { @@ -4041,17 +4071,17 @@ if (ObjCExpr) { StrE = ObjCExpr->getString(); - result->EvalType = CXEval_ObjCStrLiteral; + result.EvalType = CXEval_ObjCStrLiteral; } else { StrE = cast<StringLiteral>(expr); - result->EvalType = CXEval_StrLiteral; + 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'; - return result.release(); + result.EvalData.stringVal = new char[strRef.size() + 1]; + strncpy((char *)result.EvalData.stringVal, strRef.c_str(), strRef.size()); + result.EvalData.stringVal[strRef.size()] = '\0'; + return true; } if (expr->getStmtClass() == Stmt::CStyleCastExprClass) { @@ -4065,13 +4095,13 @@ StringLiteral *S = getCFSTR_value(callExpr); if (S) { std::string strLiteral(S->getString().str()); - result->EvalType = CXEval_CFStr; + result.EvalType = CXEval_CFStr; - result->EvalData.stringVal = new char[strLiteral.size() + 1]; - strncpy((char *)result->EvalData.stringVal, strLiteral.c_str(), + result.EvalData.stringVal = new char[strLiteral.size() + 1]; + strncpy((char *)result.EvalData.stringVal, strLiteral.c_str(), strLiteral.size()); - result->EvalData.stringVal[strLiteral.size()] = '\0'; - return result.release(); + result.EvalData.stringVal[strLiteral.size()] = '\0'; + return true; } } @@ -4080,23 +4110,23 @@ rettype = callExpr->getCallReturnType(ctx); if (rettype->isVectorType() || callExpr->getNumArgs() > 1) - return nullptr; + return false; if (rettype->isIntegralType(ctx) || rettype->isRealFloatingType()) { if (callExpr->getNumArgs() == 1 && !callExpr->getArg(0)->getType()->isIntegralType(ctx)) - return nullptr; + return false; } else if (rettype.getAsString() == "CFStringRef") { 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(), + 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'; - return result.release(); + result.EvalData.stringVal[strLiteral.size()] = '\0'; + return true; } } } else if (expr->getStmtClass() == Stmt::DeclRefExprClass) { @@ -4104,14 +4134,53 @@ 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.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'; + return true; + } + } + + return false; +} + +static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) { + Expr::EvalResult ER; + ASTContext &ctx = getCursorContext(C); + if (!expr) + return nullptr; + + expr = expr->IgnoreParens(); + if (expr->isValueDependent()) + return nullptr; + if (!expr->EvaluateAsRValue(ER, ctx)) + return nullptr; + + auto result = std::make_unique<ExprEvalResult>(); + if (evaluateVal(ER.Val, *result)) { + return result.release(); + } + + if (expr->getStmtClass() == Stmt::InitListExprClass) { + const InitListExpr *I = dyn_cast<InitListExpr>(expr); + // evaluateVal() has already determined that we have CXEval_Array, but some + // evaluations failed (because they are not CXEval_Int, CXEval_Float, etc.) + // so try evaluating the elements as strings + if (result->EvalType == CXEval_Array && + result->EvalData.arrayVal.size >= I->getNumInits()) { + for (unsigned i = 0; i < I->getNumInits(); i++) { + evaluateStringExpr(const_cast<Expr *>(I->getInit(i)), ctx, + result->EvalData.arrayVal.elts[i]); + } return result.release(); } } + if (evaluateStringExpr(expr, ctx, *result)) { + return result.release(); + } + return nullptr; } Index: clang/tools/c-index-test/c-index-test.c =================================================================== --- clang/tools/c-index-test/c-index-test.c +++ clang/tools/c-index-test/c-index-test.c @@ -3019,6 +3019,20 @@ printf("Kind: CFString , Value: %s", str); break; } + case CXEval_Array: + { + printf("Kind: Array , Value: { "); + for (unsigned i = 0; i < clang_EvalResult_getArraySize(result); i++) { + if (i > 0) { + printf(" , "); + } + printf("[%d]: { ", i); + display_evaluate_results(clang_EvalResult_getArrayElt(result, i)); + printf(" }"); + } + printf(" }"); + break; + } default: printf("Unexposed"); break; Index: clang/test/Index/evaluate-cursor.cpp =================================================================== --- clang/test/Index/evaluate-cursor.cpp +++ clang/test/Index/evaluate-cursor.cpp @@ -35,6 +35,11 @@ [&arr] {}; } +class z { + int array[4]{1, 2, 3, 4}; + const char* str[2]{ "hello", "world" }; +}; + // RUN: c-index-test -evaluate-cursor-at=%s:4:7 \ // RUN: -evaluate-cursor-at=%s:8:7 \ // RUN: -evaluate-cursor-at=%s:8:11 -std=c++11 %s | FileCheck %s @@ -75,3 +80,15 @@ // RUN: c-index-test -evaluate-cursor-at=%s:35:5 \ // RUN: -std=c++11 %s | FileCheck -check-prefix=VLA %s // VLA: Not Evaluatable + +// RUN: c-index-test -evaluate-cursor-at=%s:39:9 \ +// RUN: -std=c++11 %s | FileCheck -check-prefix=CHECK-ARRAY %s +// CHECK-ARRAY: [0]: { Kind: Int, Value: 1 } +// CHECK-ARRAY: [1]: { Kind: Int, Value: 2 } +// CHECK-ARRAY: [2]: { Kind: Int, Value: 3 } +// CHECK-ARRAY: [3]: { Kind: Int, Value: 4 } + +// RUN: c-index-test -evaluate-cursor-at=%s:40:15 \ +// RUN: -std=c++11 %s | FileCheck -check-prefix=CHECK-ARRAY-STR %s +// CHECK-ARRAY-STR: [0]: { Kind: CString , Value: hello } +// CHECK-ARRAY-STR: [1]: { Kind: CString , Value: world } Index: clang/include/clang-c/Index.h =================================================================== --- clang/include/clang-c/Index.h +++ clang/include/clang-c/Index.h @@ -33,7 +33,7 @@ * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable. */ #define CINDEX_VERSION_MAJOR 0 -#define CINDEX_VERSION_MINOR 62 +#define CINDEX_VERSION_MINOR 63 #define CINDEX_VERSION_ENCODE(major, minor) (((major)*10000) + ((minor)*1)) @@ -5982,6 +5982,7 @@ CXEval_StrLiteral = 4, CXEval_CFStr = 5, CXEval_Other = 6, + CXEval_Array = 7, CXEval_UnExposed = 0 @@ -6037,6 +6038,11 @@ */ CINDEX_LINKAGE double clang_EvalResult_getAsDouble(CXEvalResult E); +/** + * Returns the number of elements if the kind is Array. + */ +CINDEX_LINKAGE unsigned clang_EvalResult_getArraySize(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, @@ -6045,6 +6051,12 @@ */ CINDEX_LINKAGE const char *clang_EvalResult_getAsStr(CXEvalResult E); +/** + * Returns an element for a Array result. User must not call + * clang_EvalResult_dispose on the return value. + */ +CINDEX_LINKAGE CXEvalResult clang_EvalResult_getArrayElt(CXEvalResult E, unsigned index); + /** * Disposes the created Eval memory. */
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits