[clang] [clang-repl] Lay the basic infrastructure for pretty printing of types (PR #148701)
Vassil Vassilev via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 14 22:16:53 PDT 2025
================
@@ -18,18 +18,341 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Interpreter/Interpreter.h"
#include "clang/Interpreter/Value.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
-
#include <cstdarg>
+#include <sstream>
+#include <string>
+
+#define DEBUG_TYPE "interp-value"
+
+using namespace clang;
+
+static std::string DeclTypeToString(const QualType &QT, NamedDecl *D) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ if (QT.hasQualifiers())
+ SS << QT.getQualifiers().getAsString() << " ";
+ SS << D->getQualifiedNameAsString();
+ return Str;
+}
+
+static std::string QualTypeToString(ASTContext &Ctx, QualType QT) {
+ PrintingPolicy Policy(Ctx.getPrintingPolicy());
+ // Print the Allocator in STL containers, for instance.
+ Policy.SuppressDefaultTemplateArgs = false;
+ Policy.SuppressUnwrittenScope = true;
+ // Print 'a<b<c> >' rather than 'a<b<c>>'.
+ Policy.SplitTemplateClosers = true;
+
+ struct LocalPrintingPolicyRAII {
+ ASTContext &Context;
+ PrintingPolicy Policy;
+
+ LocalPrintingPolicyRAII(ASTContext &Ctx, PrintingPolicy &PP)
+ : Context(Ctx), Policy(Ctx.getPrintingPolicy()) {
+ Context.setPrintingPolicy(PP);
+ }
+ ~LocalPrintingPolicyRAII() { Context.setPrintingPolicy(Policy); }
+ } X(Ctx, Policy);
+
+ const QualType NonRefTy = QT.getNonReferenceType();
+
+ if (const auto *TTy = llvm::dyn_cast<TagType>(NonRefTy))
+ return DeclTypeToString(NonRefTy, TTy->getDecl());
+
+ if (const auto *TRy = dyn_cast<RecordType>(NonRefTy))
+ return DeclTypeToString(NonRefTy, TRy->getDecl());
+
+ const QualType Canon = NonRefTy.getCanonicalType();
+
+ // FIXME: How a builtin type can be a function pointer type?
+ if (Canon->isBuiltinType() && !NonRefTy->isFunctionPointerType() &&
+ !NonRefTy->isMemberPointerType())
+ return Canon.getAsString(Ctx.getPrintingPolicy());
+
+ if (const auto *TDTy = dyn_cast<TypedefType>(NonRefTy)) {
+ // FIXME: TemplateSpecializationType & SubstTemplateTypeParmType checks
+ // are predominately to get STL containers to print nicer and might be
+ // better handled in GetFullyQualifiedName.
+ //
+ // std::vector<Type>::iterator is a TemplateSpecializationType
+ // std::vector<Type>::value_type is a SubstTemplateTypeParmType
+ //
+ QualType SSDesugar = TDTy->getLocallyUnqualifiedSingleStepDesugaredType();
+ if (llvm::isa<SubstTemplateTypeParmType>(SSDesugar))
+ return GetFullTypeName(Ctx, Canon);
+ else if (llvm::isa<TemplateSpecializationType>(SSDesugar))
+ return GetFullTypeName(Ctx, NonRefTy);
+ return DeclTypeToString(NonRefTy, TDTy->getDecl());
+ }
+ return GetFullTypeName(Ctx, NonRefTy);
+}
+
+static std::string EnumToString(const Value &V) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext());
+
+ QualType DesugaredTy = V.getType().getDesugaredType(Ctx);
+ const EnumType *EnumTy = DesugaredTy.getNonReferenceType()->getAs<EnumType>();
+ assert(EnumTy && "Fail to cast to enum type");
+
+ EnumDecl *ED = EnumTy->getDecl();
+ uint64_t Data = V.getULongLong();
+ bool IsFirst = true;
+ llvm::APSInt AP = Ctx.MakeIntValue(Data, DesugaredTy);
+
+ for (auto I = ED->enumerator_begin(), E = ED->enumerator_end(); I != E; ++I) {
+ if (I->getInitVal() == AP) {
+ if (!IsFirst)
+ SS << " ? ";
+ SS << "(" + I->getQualifiedNameAsString() << ")";
+ IsFirst = false;
+ }
+ }
+ llvm::SmallString<64> APStr;
+ AP.toString(APStr, /*Radix=*/10);
+ SS << " : " << QualTypeToString(Ctx, ED->getIntegerType()) << " " << APStr;
+ return Str;
+}
+
+static std::string FunctionToString(const Value &V, const void *Ptr) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << "Function @" << Ptr;
+
+ const DeclContext *PTU = V.getASTContext().getTranslationUnitDecl();
+ // Find the last top-level-stmt-decl. This is a forward iterator but the
+ // partial translation unit should not be large.
+ const TopLevelStmtDecl *TLSD = nullptr;
+ for (const Decl *D : PTU->noload_decls())
+ if (isa<TopLevelStmtDecl>(D))
+ TLSD = cast<TopLevelStmtDecl>(D);
+
+ // Get __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void
+ // *OpaqueType, void *Val);
+ const FunctionDecl *FD = nullptr;
+ if (auto *InterfaceCall = llvm::dyn_cast<CallExpr>(TLSD->getStmt())) {
+ const auto *Arg = InterfaceCall->getArg(/*Val*/ 3);
+ // Get rid of cast nodes.
+ while (const CastExpr *CastE = llvm::dyn_cast<CastExpr>(Arg))
+ Arg = CastE->getSubExpr();
+ if (const DeclRefExpr *DeclRefExp = llvm::dyn_cast<DeclRefExpr>(Arg))
+ FD = llvm::dyn_cast<FunctionDecl>(DeclRefExp->getDecl());
+
+ if (FD) {
+ SS << '\n';
+ const clang::FunctionDecl *FDef;
+ if (FD->hasBody(FDef))
+ FDef->print(SS);
+ }
+ }
+ return Str;
+}
+
+static std::string AddressToString(const void *Ptr, char Prefix) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ if (!Ptr)
+ return Str;
+ SS << Prefix << Ptr;
+ return Str;
+}
+
+static std::string CharPtrToString(const char *Ptr) {
+ if (!Ptr)
+ return "0";
+
+ std::string result = "\"";
+ result += Ptr;
+ result += '"';
+ return result;
+}
namespace clang {
+struct ValueRef : public Value {
+ ValueRef(Interpreter *In, void *Ty) : Value(In, Ty) {
+ // Tell the base class to not try to deallocate if it manages the value.
+ IsManuallyAlloc = false;
+ }
+ void setData(long double D) { Data.m_LongDouble = D; }
+};
+
+std::string Interpreter::ValueDataToString(const Value &V) {
+ Sema &S = getCompilerInstance()->getSema();
+ ASTContext &Ctx = S.getASTContext();
+
+ QualType QT = V.getType();
+
+ if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(QT)) {
+ QualType ElemTy = CAT->getElementType();
+ size_t ElemCount = Ctx.getConstantArrayElementCount(CAT);
+ const Type *BaseTy = CAT->getBaseElementTypeUnsafe();
+ size_t ElemSize = Ctx.getTypeSizeInChars(BaseTy).getQuantity();
+
+ // Treat null terminated char arrays as strings basically.
+ if (ElemTy->isCharType()) {
+ char last = *(char *)(((uintptr_t)V.getPtr()) + ElemCount * ElemSize - 1);
+ if (last == '\0')
+ return CharPtrToString((char *)V.getPtr());
+ }
+
+ std::string result = "{ ";
----------------
vgvassilev wrote:
I think I addressed this.
https://github.com/llvm/llvm-project/pull/148701
More information about the cfe-commits
mailing list