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

Reply via email to