diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -625,6 +625,98 @@ return V.convertToDouble(); } +static bool TryPrintAsStringLiteral(raw_ostream &Out, const ArrayType *ATy, + const APValue *Data, size_t Size) { + if (Size == 0) + return false; + + QualType Ty = ATy->getElementType(); + if (!Ty->isAnyCharacterType()) + return false; + + // Nothing we can do about a sequence that is not null-terminated + if (!Data[--Size].getInt().isZero()) + return false; + + constexpr size_t MaxN = 36; + char Buf[MaxN * 2 + 3] = {'"'}; // "At most 36 escaped chars" + \0 + auto *pBuf = Buf + 1; + + // Better than printing a two-digit sequence of 10 integers. + StringRef Ellipsis; + if (Size > MaxN) { + Ellipsis = "[...]"; + Size = std::min(MaxN - Ellipsis.size(), Size); + } + + auto writeEscape = [](char *Ptr, char Ch) { + Ptr[0] = '\\'; + Ptr[1] = Ch; + return Ptr + 2; + }; + + for (auto &Val : ArrayRef(Data, Size)) { + auto Char64 = Val.getInt().getExtValue(); + if (Char64 > 0x7f) + return false; // Bye bye, see you in integers. + switch (auto Ch = static_cast(Char64)) { + default: + if (isPrintable(Ch)) { + *pBuf++ = Ch; + break; + } + return false; + case '\\': + case '\'': // The diagnostic message is 'quoted' + case '"': + pBuf = writeEscape(pBuf, Ch); + break; + case '\0': + pBuf = writeEscape(pBuf, '0'); + break; + case '\a': + pBuf = writeEscape(pBuf, 'a'); + break; + case '\b': + pBuf = writeEscape(pBuf, 'b'); + break; + case '\f': + pBuf = writeEscape(pBuf, 'f'); + break; + case '\n': + pBuf = writeEscape(pBuf, 'n'); + break; + case '\r': + pBuf = writeEscape(pBuf, 'r'); + break; + case '\t': + pBuf = writeEscape(pBuf, 't'); + break; + case '\v': + pBuf = writeEscape(pBuf, 'v'); + break; + } + } + + if (!Ellipsis.empty()) { + memcpy(pBuf, Ellipsis.data(), Ellipsis.size()); + pBuf += Ellipsis.size(); + } + *pBuf++ = '"'; + + if (Ty->isWideCharType()) + Out << 'L'; + else if (Ty->isChar8Type()) + Out << "u8"; + else if (Ty->isChar16Type()) + Out << 'u'; + else if (Ty->isChar32Type()) + Out << 'U'; + + Out << StringRef(Buf, pBuf - Buf); + return true; +} + void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, QualType Ty) const { printPretty(Out, Ctx.getPrintingPolicy(), Ty, &Ctx); @@ -795,17 +887,24 @@ } case APValue::Array: { const ArrayType *AT = Ty->castAsArrayTypeUnsafe(); + unsigned N = getArrayInitializedElts(); + if (N != 0 && + TryPrintAsStringLiteral(Out, AT, &getArrayInitializedElt(0), N)) + return; QualType ElemTy = AT->getElementType(); Out << '{'; - if (unsigned N = getArrayInitializedElts()) { - getArrayInitializedElt(0).printPretty(Out, Policy, ElemTy, Ctx); - for (unsigned I = 1; I != N; ++I) { + unsigned I = 0; + switch (N) { + case 0: + for (; I != N; ++I) { Out << ", "; if (I == 10) { // Avoid printing out the entire contents of large arrays. - Out << "..."; - break; + Out << "...}"; + return; } + LLVM_FALLTHROUGH; + default: getArrayInitializedElt(I).printPretty(Out, Policy, ElemTy, Ctx); } } diff --git a/clang/test/SemaTemplate/temp_arg_string_printing.cpp b/clang/test/SemaTemplate/temp_arg_string_printing.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaTemplate/temp_arg_string_printing.cpp @@ -0,0 +1,137 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -ast-print %s | FileCheck %s + +using size_t = __SIZE_TYPE__; +static_assert(__has_builtin(__make_integer_seq)); + +template class idx_seq {}; +template using make_idx_seq = __make_integer_seq; + +template +struct Str { + constexpr Str(CharT const (&s)[N]) : Str(s, make_idx_seq()) {} + CharT value[N]; + +private: + template + constexpr Str(CharT const (&s)[N], idx_seq) : value{s[I]...} {} +}; + +template class ASCII {}; + +void narrow() { + // CHECK{LITERAL}: ASCII<{""}> + new ASCII<"">; + // CHECK{LITERAL}: ASCII<{"the quick brown fox jumps"}> + new ASCII<"the quick brown fox jumps">; + // CHECK{LITERAL}: ASCII<{"OVER THE LAZY DOG 0123456789"}> + new ASCII<"OVER THE LAZY DOG 0123456789">; + // CHECK{LITERAL}: ASCII<{"\\`~!@#$%^&*()_+-={}[]|\'\";:,.<>?/"}> + new ASCII?/)">; + // CHECK{LITERAL}: ASCII<{"escape\0"}> + new ASCII<"escape\0">; + // CHECK{LITERAL}: ASCII<{"escape\r\n"}> + new ASCII<"escape\r\n">; + // CHECK{LITERAL}: ASCII<{"escape\\\t\'\f\v"}> + new ASCII<"escape\\\t'\f\v">; + // CHECK{LITERAL}: ASCII<{"escape\a\bc"}> + new ASCII<"escape\a\b\c">; + // CHECK{LITERAL}: ASCII<{{110, 111, 116, 17, 0}}> + new ASCII<"not\x11">; + // CHECK{LITERAL}: ASCII<{{18, 20, 127, 16, 1, 32, 97, 98, 99, 0}}> + new ASCII<"\x12\x14\x7f\x10\x01 abc">; + // CHECK{LITERAL}: ASCII<{{18, 20, 127, 16, 1, 32, 97, 98, 99, 100, ...}}> + new ASCII<"\x12\x14\x7f\x10\x01 abcd">; + // CHECK{LITERAL}: ASCII<{"print more characters as string"}> + new ASCII<"print more characters as string">; + // CHECK{LITERAL}: ASCII<{"print even more characters as string"}> + new ASCII<"print even more characters as string">; + // CHECK{LITERAL}: ASCII<{"print many characters no more t[...]"}> + new ASCII<"print many characters no more than a limit">; + // CHECK{LITERAL}: ASCII<{"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}> + new ASCII<"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0">; + // CHECK{LITERAL}: ASCII<{"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n[...]"}> + new ASCII<"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n">; +} + +void wide() { + // CHECK{LITERAL}: ASCII<{L""}> + new ASCII; + // CHECK{LITERAL}: ASCII<{L"the quick brown fox jumps"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{L"OVER THE LAZY DOG 0123456789"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{L"\\`~!@#$%^&*()_+-={}[]|\'\";:,.<>?/"}> + new ASCII?/)">; + // CHECK{LITERAL}: ASCII<{L"escape\0"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{L"escape\r\n"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{L"escape\\\t\f\v"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{L"escape\a\bc"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{{110, 111, 116, 17, 0}}> + new ASCII; + // CHECK{LITERAL}: ASCII<{{18, 20, 255, 22909, 136, 32, 97, 98, 99, 0}}> + new ASCII; + // CHECK{LITERAL}: ASCII<{{18, 20, 255, 22909, 136, 32, 97, 98, 99, 100, ...}}> + new ASCII; + // CHECK{LITERAL}: ASCII<{L"print more characters as string"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{L"print even more characters as string"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{L"print many characters no more t[...]"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{L"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n[...]"}> + new ASCII; +} + +void utf8() +{ + // CHECK{LITERAL}: ASCII<{u8""}> + new ASCII; + // CHECK{LITERAL}: ASCII<{u8"\\`~!@#$%^&*()_+-={}[]|\'\";:,.<>?/"}> + new ASCII?/)">; + // CHECK{LITERAL}: ASCII<{u8"escape\0"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{u8"escape\r\n"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{{229, 165, 189, 239, 191, 189, 0}}> + new ASCII; + // CHECK{LITERAL}: ASCII<{u8"print many characters no more t[...]"}> + new ASCII; +} + +void utf16() +{ + // CHECK{LITERAL}: ASCII<{u""}> + new ASCII; + // CHECK{LITERAL}: ASCII<{u"\\`~!@#$%^&*()_+-={}[]|\'\";:,.<>?/"}> + new ASCII?/)">; + // CHECK{LITERAL}: ASCII<{u"escape\0"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{u"escape\r\n"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{{22909, 65533, 0}}> + new ASCII; + // CHECK{LITERAL}: ASCII<{u"print many characters no more t[...]"}> + new ASCII; +} + +void utf32() +{ + // CHECK{LITERAL}: ASCII<{U""}> + new ASCII; + // CHECK{LITERAL}: ASCII<{U"\\`~!@#$%^&*()_+-={}[]|\'\";:,.<>?/"}> + new ASCII?/)">; + // CHECK{LITERAL}: ASCII<{U"escape\0"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{U"escape\r\n"}> + new ASCII; + // CHECK{LITERAL}: ASCII<{{22909, 131358, 0}}> + new ASCII; + // CHECK{LITERAL}: ASCII<{U"print many characters no more t[...]"}> + new ASCII; +}