forked from OSchip/llvm-project
[AST] Print NTTP args as string-literals when possible
C++20 non-type template parameter prints `MyType<{{116, 104, 105, 115}}>` when the code is as simple as `MyType<"this">`. This patch prints `MyType<{"this"}>`, with one layer of braces preserved for the intermediate structural type to trigger CTAD. `StringLiteral` handles this case, but `StringLiteral` inside `APValue` code looks like a circular dependency. The proposed patch implements a cheap strategy to emit string literals in diagnostic messages only when they are readable and fall back to integer sequences. Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D115031
This commit is contained in:
parent
9c6250ee41
commit
44eee659f1
|
@ -3313,10 +3313,12 @@ public:
|
|||
|
||||
/// Print this object as an equivalent expression.
|
||||
void printAsExpr(llvm::raw_ostream &OS) const;
|
||||
void printAsExpr(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const;
|
||||
|
||||
/// Print this object as an initializer suitable for a variable of the
|
||||
/// object's type.
|
||||
void printAsInit(llvm::raw_ostream &OS) const;
|
||||
void printAsInit(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const;
|
||||
|
||||
const APValue &getValue() const { return Value; }
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ struct PrintingPolicy {
|
|||
SuppressImplicitBase(false), FullyQualifiedName(false),
|
||||
PrintCanonicalTypes(false), PrintInjectedClassNameWithArguments(true),
|
||||
UsePreferredNames(true), AlwaysIncludeTypeForTemplateArgument(false),
|
||||
CleanUglifiedParameters(false) {}
|
||||
CleanUglifiedParameters(false), EntireContentsOfLargeArray(true) {}
|
||||
|
||||
/// Adjust this printing policy for cases where it's known that we're
|
||||
/// printing C++ code (for instance, if AST dumping reaches a C++-only
|
||||
|
@ -286,6 +286,10 @@ struct PrintingPolicy {
|
|||
/// This only affects parameter names, and so describes a compatible API.
|
||||
unsigned CleanUglifiedParameters : 1;
|
||||
|
||||
/// Whether to print the entire array initializers, especially on non-type
|
||||
/// template parameters, no matter how many elements there are.
|
||||
unsigned EntireContentsOfLargeArray : 1;
|
||||
|
||||
/// Callbacks to use to allow the behavior of printing to be customized.
|
||||
const PrintingCallbacks *Callbacks = nullptr;
|
||||
};
|
||||
|
|
|
@ -38,15 +38,16 @@ namespace charinfo {
|
|||
};
|
||||
} // end namespace charinfo
|
||||
|
||||
/// Returns true if this is an ASCII character.
|
||||
/// Returns true if a byte is an ASCII character.
|
||||
LLVM_READNONE inline bool isASCII(char c) {
|
||||
return static_cast<unsigned char>(c) <= 127;
|
||||
}
|
||||
|
||||
LLVM_READNONE inline bool isASCII(unsigned char c) { return c <= 127; }
|
||||
|
||||
/// Returns true if this is an ASCII character.
|
||||
/// Returns true if a codepoint is an ASCII character.
|
||||
LLVM_READNONE inline bool isASCII(uint32_t c) { return c <= 127; }
|
||||
LLVM_READNONE inline bool isASCII(int64_t c) { return 0 <= c && c <= 127; }
|
||||
|
||||
/// Returns true if this is a valid first character of a C identifier,
|
||||
/// which is [a-zA-Z_].
|
||||
|
@ -162,6 +163,44 @@ LLVM_READONLY inline bool isRawStringDelimBody(unsigned char c) {
|
|||
CHAR_DIGIT|CHAR_UNDER|CHAR_RAWDEL)) != 0;
|
||||
}
|
||||
|
||||
enum class EscapeChar {
|
||||
Single = 1,
|
||||
Double = 2,
|
||||
SingleAndDouble = static_cast<int>(Single) | static_cast<int>(Double),
|
||||
};
|
||||
|
||||
/// Return C-style escaped string for special characters, or an empty string if
|
||||
/// there is no such mapping.
|
||||
template <EscapeChar Opt, class CharT>
|
||||
LLVM_READONLY inline auto escapeCStyle(CharT Ch) -> StringRef {
|
||||
switch (Ch) {
|
||||
case '\\':
|
||||
return "\\\\";
|
||||
case '\'':
|
||||
if ((static_cast<int>(Opt) & static_cast<int>(EscapeChar::Single)) == 0)
|
||||
break;
|
||||
return "\\'";
|
||||
case '"':
|
||||
if ((static_cast<int>(Opt) & static_cast<int>(EscapeChar::Double)) == 0)
|
||||
break;
|
||||
return "\\\"";
|
||||
case '\a':
|
||||
return "\\a";
|
||||
case '\b':
|
||||
return "\\b";
|
||||
case '\f':
|
||||
return "\\f";
|
||||
case '\n':
|
||||
return "\\n";
|
||||
case '\r':
|
||||
return "\\r";
|
||||
case '\t':
|
||||
return "\\t";
|
||||
case '\v':
|
||||
return "\\v";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Converts the given ASCII character to its lowercase equivalent.
|
||||
///
|
||||
|
|
|
@ -625,6 +625,67 @@ static double GetApproxValue(const llvm::APFloat &F) {
|
|||
return V.convertToDouble();
|
||||
}
|
||||
|
||||
static bool TryPrintAsStringLiteral(raw_ostream &Out,
|
||||
const PrintingPolicy &Policy,
|
||||
const ArrayType *ATy,
|
||||
ArrayRef<APValue> Inits) {
|
||||
if (Inits.empty())
|
||||
return false;
|
||||
|
||||
QualType Ty = ATy->getElementType();
|
||||
if (!Ty->isAnyCharacterType())
|
||||
return false;
|
||||
|
||||
// Nothing we can do about a sequence that is not null-terminated
|
||||
if (!Inits.back().getInt().isZero())
|
||||
return false;
|
||||
else
|
||||
Inits = Inits.drop_back();
|
||||
|
||||
llvm::SmallString<40> Buf;
|
||||
Buf.push_back('"');
|
||||
|
||||
// Better than printing a two-digit sequence of 10 integers.
|
||||
constexpr size_t MaxN = 36;
|
||||
StringRef Ellipsis;
|
||||
if (Inits.size() > MaxN && !Policy.EntireContentsOfLargeArray) {
|
||||
Ellipsis = "[...]";
|
||||
Inits =
|
||||
Inits.take_front(std::min(MaxN - Ellipsis.size() / 2, Inits.size()));
|
||||
}
|
||||
|
||||
for (auto &Val : Inits) {
|
||||
int64_t Char64 = Val.getInt().getExtValue();
|
||||
if (!isASCII(Char64))
|
||||
return false; // Bye bye, see you in integers.
|
||||
auto Ch = static_cast<unsigned char>(Char64);
|
||||
// The diagnostic message is 'quoted'
|
||||
StringRef Escaped = escapeCStyle<EscapeChar::SingleAndDouble>(Ch);
|
||||
if (Escaped.empty()) {
|
||||
if (!isPrintable(Ch))
|
||||
return false;
|
||||
Buf.emplace_back(Ch);
|
||||
} else {
|
||||
Buf.append(Escaped);
|
||||
}
|
||||
}
|
||||
|
||||
Buf.append(Ellipsis);
|
||||
Buf.push_back('"');
|
||||
|
||||
if (Ty->isWideCharType())
|
||||
Out << 'L';
|
||||
else if (Ty->isChar8Type())
|
||||
Out << "u8";
|
||||
else if (Ty->isChar16Type())
|
||||
Out << 'u';
|
||||
else if (Ty->isChar32Type())
|
||||
Out << 'U';
|
||||
|
||||
Out << Buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx,
|
||||
QualType Ty) const {
|
||||
printPretty(Out, Ctx.getPrintingPolicy(), Ty, &Ctx);
|
||||
|
@ -795,17 +856,23 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy,
|
|||
}
|
||||
case APValue::Array: {
|
||||
const ArrayType *AT = Ty->castAsArrayTypeUnsafe();
|
||||
unsigned N = getArrayInitializedElts();
|
||||
if (N != 0 && TryPrintAsStringLiteral(Out, Policy, 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;
|
||||
if (I == 10 && !Policy.EntireContentsOfLargeArray) {
|
||||
Out << "...}";
|
||||
return;
|
||||
}
|
||||
LLVM_FALLTHROUGH;
|
||||
default:
|
||||
getArrayInitializedElt(I).printPretty(Out, Policy, ElemTy, Ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1874,7 +1874,7 @@ class TemplateDiff {
|
|||
// FIXME: Diffing the APValue would be neat.
|
||||
// FIXME: Suppress this and use the full name of the declaration if the
|
||||
// parameter is a pointer or reference.
|
||||
TPO->printAsInit(OS);
|
||||
TPO->printAsInit(OS, Policy);
|
||||
return;
|
||||
}
|
||||
VD->printName(OS);
|
||||
|
|
|
@ -1514,12 +1514,20 @@ void TemplateParamObjectDecl::printName(llvm::raw_ostream &OS) const {
|
|||
}
|
||||
|
||||
void TemplateParamObjectDecl::printAsExpr(llvm::raw_ostream &OS) const {
|
||||
const ASTContext &Ctx = getASTContext();
|
||||
getType().getUnqualifiedType().print(OS, Ctx.getPrintingPolicy());
|
||||
printAsInit(OS);
|
||||
printAsExpr(OS, getASTContext().getPrintingPolicy());
|
||||
}
|
||||
|
||||
void TemplateParamObjectDecl::printAsExpr(llvm::raw_ostream &OS,
|
||||
const PrintingPolicy &Policy) const {
|
||||
getType().getUnqualifiedType().print(OS, Policy);
|
||||
printAsInit(OS, Policy);
|
||||
}
|
||||
|
||||
void TemplateParamObjectDecl::printAsInit(llvm::raw_ostream &OS) const {
|
||||
const ASTContext &Ctx = getASTContext();
|
||||
getValue().printPretty(OS, Ctx, getType());
|
||||
printAsInit(OS, getASTContext().getPrintingPolicy());
|
||||
}
|
||||
|
||||
void TemplateParamObjectDecl::printAsInit(llvm::raw_ostream &OS,
|
||||
const PrintingPolicy &Policy) const {
|
||||
getValue().printPretty(OS, Policy, getType(), &getASTContext());
|
||||
}
|
||||
|
|
|
@ -960,40 +960,10 @@ void CharacterLiteral::print(unsigned Val, CharacterKind Kind,
|
|||
break;
|
||||
}
|
||||
|
||||
switch (Val) {
|
||||
case '\\':
|
||||
OS << "'\\\\'";
|
||||
break;
|
||||
case '\'':
|
||||
OS << "'\\''";
|
||||
break;
|
||||
case '\a':
|
||||
// TODO: K&R: the meaning of '\\a' is different in traditional C
|
||||
OS << "'\\a'";
|
||||
break;
|
||||
case '\b':
|
||||
OS << "'\\b'";
|
||||
break;
|
||||
// Nonstandard escape sequence.
|
||||
/*case '\e':
|
||||
OS << "'\\e'";
|
||||
break;*/
|
||||
case '\f':
|
||||
OS << "'\\f'";
|
||||
break;
|
||||
case '\n':
|
||||
OS << "'\\n'";
|
||||
break;
|
||||
case '\r':
|
||||
OS << "'\\r'";
|
||||
break;
|
||||
case '\t':
|
||||
OS << "'\\t'";
|
||||
break;
|
||||
case '\v':
|
||||
OS << "'\\v'";
|
||||
break;
|
||||
default:
|
||||
StringRef Escaped = escapeCStyle<EscapeChar::Single>(Val);
|
||||
if (!Escaped.empty()) {
|
||||
OS << "'" << Escaped << "'";
|
||||
} else {
|
||||
// A character literal might be sign-extended, which
|
||||
// would result in an invalid \U escape sequence.
|
||||
// FIXME: multicharacter literals such as '\xFF\xFF\xFF\xFF'
|
||||
|
@ -1163,8 +1133,9 @@ void StringLiteral::outputString(raw_ostream &OS) const {
|
|||
|
||||
unsigned LastSlashX = getLength();
|
||||
for (unsigned I = 0, N = getLength(); I != N; ++I) {
|
||||
switch (uint32_t Char = getCodeUnit(I)) {
|
||||
default:
|
||||
uint32_t Char = getCodeUnit(I);
|
||||
StringRef Escaped = escapeCStyle<EscapeChar::Double>(Char);
|
||||
if (Escaped.empty()) {
|
||||
// FIXME: Convert UTF-8 back to codepoints before rendering.
|
||||
|
||||
// Convert UTF-16 surrogate pairs back to codepoints before rendering.
|
||||
|
@ -1192,7 +1163,7 @@ void StringLiteral::outputString(raw_ostream &OS) const {
|
|||
for (/**/; Shift >= 0; Shift -= 4)
|
||||
OS << Hex[(Char >> Shift) & 15];
|
||||
LastSlashX = I;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Char > 0xffff)
|
||||
|
@ -1205,7 +1176,7 @@ void StringLiteral::outputString(raw_ostream &OS) const {
|
|||
<< Hex[(Char >> 8) & 15]
|
||||
<< Hex[(Char >> 4) & 15]
|
||||
<< Hex[(Char >> 0) & 15];
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we used \x... for the previous character, and this character is a
|
||||
|
@ -1230,17 +1201,9 @@ void StringLiteral::outputString(raw_ostream &OS) const {
|
|||
<< (char)('0' + ((Char >> 6) & 7))
|
||||
<< (char)('0' + ((Char >> 3) & 7))
|
||||
<< (char)('0' + ((Char >> 0) & 7));
|
||||
break;
|
||||
// Handle some common non-printable cases to make dumps prettier.
|
||||
case '\\': OS << "\\\\"; break;
|
||||
case '"': OS << "\\\""; break;
|
||||
case '\a': OS << "\\a"; break;
|
||||
case '\b': OS << "\\b"; break;
|
||||
case '\f': OS << "\\f"; break;
|
||||
case '\n': OS << "\\n"; break;
|
||||
case '\r': OS << "\\r"; break;
|
||||
case '\t': OS << "\\t"; break;
|
||||
case '\v': OS << "\\v"; break;
|
||||
} else {
|
||||
// Handle some common non-printable cases to make dumps prettier.
|
||||
OS << Escaped;
|
||||
}
|
||||
}
|
||||
OS << '"';
|
||||
|
|
|
@ -1023,7 +1023,7 @@ void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) {
|
|||
return;
|
||||
}
|
||||
if (const auto *TPOD = dyn_cast<TemplateParamObjectDecl>(Node->getDecl())) {
|
||||
TPOD->printAsExpr(OS);
|
||||
TPOD->printAsExpr(OS, Policy);
|
||||
return;
|
||||
}
|
||||
if (NestedNameSpecifier *Qualifier = Node->getQualifier())
|
||||
|
|
|
@ -434,7 +434,7 @@ void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out,
|
|||
NamedDecl *ND = getAsDecl();
|
||||
if (getParamTypeForDecl()->isRecordType()) {
|
||||
if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) {
|
||||
TPO->printAsInit(Out);
|
||||
TPO->printAsInit(Out, Policy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,6 +114,9 @@ PrintingPolicy Sema::getPrintingPolicy(const ASTContext &Context,
|
|||
}
|
||||
}
|
||||
|
||||
// Shorten the data output if needed
|
||||
Policy.EntireContentsOfLargeArray = false;
|
||||
|
||||
return Policy;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify
|
||||
|
||||
template <int N> struct Str {
|
||||
constexpr Str(char const (&s)[N]) { __builtin_memcpy(value, s, N); }
|
||||
char value[N];
|
||||
};
|
||||
|
||||
template <Str V> class ASCII {};
|
||||
|
||||
void Foo(ASCII<"this nontype template argument is too long to print">); // expected-note {{no known conversion from 'ASCII<{"this nontype template argument is too long"}>' to 'ASCII<{"this nontype template argument is too long to print"}>'}}
|
||||
void Bar(ASCII<"this nttp argument is too short">); // expected-note {{no known conversion from 'ASCII<{{119, 97, 105, 116, 32, 97, 32, 115, 27, 99, 111, 110, 100, 0}}>' to 'ASCII<{"this nttp argument is too short"}>'}}
|
||||
void Meow(ASCII<"what|">); // expected-note {{no known conversion from 'ASCII<{"what??!"}>' to 'ASCII<{"what|"}>' for 1st argument}}
|
||||
|
||||
void test_ascii() {
|
||||
ASCII<"this nontype template argument"
|
||||
" is too long">
|
||||
a;
|
||||
Foo(a); // expected-error {{no matching function}}
|
||||
decltype(a)::display(); // expected-error {{no member named 'display' in 'ASCII<{"this nontype template argument is [...]"}>'}}
|
||||
}
|
||||
|
||||
void test_non_ascii() {
|
||||
ASCII<"wait a s\033cond"> a;
|
||||
Bar(a); // expected-error {{no matching function}}
|
||||
decltype(a)::display(); // expected-error {{no member named 'display' in 'ASCII<{{119, 97, 105, 116, 32, 97, 32, 115, 27, 99, ...}}>'}}
|
||||
}
|
||||
|
||||
// The dialects (C++20 and above) that accept string literals as non-type
|
||||
// template arguments do not support trigraphs.
|
||||
void test_trigraph() {
|
||||
ASCII<"what??!"> a; // expected-warning {{trigraph ignored}}
|
||||
Meow(a); // expected-error {{no matching function}}
|
||||
decltype(a)::display(); // expected-error {{no member named 'display' in 'ASCII<{"what??!"}>'}}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
// 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 T, T... I> class idx_seq {};
|
||||
template <size_t N> using make_idx_seq = __make_integer_seq<idx_seq, size_t, N>;
|
||||
|
||||
template <class CharT, size_t N>
|
||||
struct Str {
|
||||
constexpr Str(CharT const (&s)[N]) : Str(s, make_idx_seq<N>()) {}
|
||||
CharT value[N];
|
||||
|
||||
private:
|
||||
template <size_t... I>
|
||||
constexpr Str(CharT const (&s)[N], idx_seq<size_t, I...>) : value{s[I]...} {}
|
||||
};
|
||||
|
||||
template <Str> class ASCII {};
|
||||
|
||||
void not_string() {
|
||||
// CHECK{LITERAL}: ASCII<{{9, -1, 42}}>
|
||||
new ASCII<(int[]){9, -1, 42}>;
|
||||
// CHECK{LITERAL}: ASCII<{{3.140000e+00, 0.000000e+00, 4.200000e+01}}>
|
||||
new ASCII<(double[]){3.14, 0., 42.}>;
|
||||
}
|
||||
|
||||
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<R"(\`~!@#$%^&*()_+-={}[]|'";:,.<>?/)">;
|
||||
// CHECK{LITERAL}: ASCII<{{101, 115, 99, 97, 112, 101, 0, 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, 0}}>
|
||||
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 more characters as string, no uplimit"}>
|
||||
new ASCII<"print more characters as string, no uplimit">;
|
||||
}
|
||||
|
||||
void wide() {
|
||||
// CHECK{LITERAL}: ASCII<{L""}>
|
||||
new ASCII<L"">;
|
||||
// CHECK{LITERAL}: ASCII<{L"the quick brown fox jumps"}>
|
||||
new ASCII<L"the quick brown fox jumps">;
|
||||
// CHECK{LITERAL}: ASCII<{L"OVER THE LAZY DOG 0123456789"}>
|
||||
new ASCII<L"OVER THE LAZY DOG 0123456789">;
|
||||
// CHECK{LITERAL}: ASCII<{L"\\`~!@#$%^&*()_+-={}[]|\'\";:,.<>?/"}>
|
||||
new ASCII<LR"(\`~!@#$%^&*()_+-={}[]|'";:,.<>?/)">;
|
||||
// CHECK{LITERAL}: ASCII<{{101, 115, 99, 97, 112, 101, 0, 0}}>
|
||||
new ASCII<L"escape\0">;
|
||||
// CHECK{LITERAL}: ASCII<{L"escape\r\n"}>
|
||||
new ASCII<L"escape\r\n">;
|
||||
// CHECK{LITERAL}: ASCII<{L"escape\\\t\f\v"}>
|
||||
new ASCII<L"escape\\\t\f\v">;
|
||||
// CHECK{LITERAL}: ASCII<{L"escape\a\bc"}>
|
||||
new ASCII<L"escape\a\b\c">;
|
||||
// CHECK{LITERAL}: ASCII<{{110, 111, 116, 17, 0}}>
|
||||
new ASCII<L"not\x11">;
|
||||
// CHECK{LITERAL}: ASCII<{{18, 20, 255, 22909, 136, 32, 97, 98, 99, 0}}>
|
||||
new ASCII<L"\x12\x14\xff\x597d\x88 abc">;
|
||||
// CHECK{LITERAL}: ASCII<{{18, 20, 255, 22909, 136, 32, 97, 98, 99, 100, 0}}>
|
||||
new ASCII<L"\x12\x14\xff\x597d\x88 abcd">;
|
||||
// CHECK{LITERAL}: ASCII<{L"print more characters as string"}>
|
||||
new ASCII<L"print more characters as string">;
|
||||
// CHECK{LITERAL}: ASCII<{L"print more characters as string, no uplimit"}>
|
||||
new ASCII<L"print more characters as string, no uplimit">;
|
||||
}
|
||||
|
||||
void utf8() {
|
||||
// CHECK{LITERAL}: ASCII<{u8""}>
|
||||
new ASCII<u8"">;
|
||||
// CHECK{LITERAL}: ASCII<{u8"\\`~!@#$%^&*()_+-={}[]|\'\";:,.<>?/"}>
|
||||
new ASCII<u8R"(\`~!@#$%^&*()_+-={}[]|'";:,.<>?/)">;
|
||||
// CHECK{LITERAL}: ASCII<{{101, 115, 99, 97, 112, 101, 0, 0}}>
|
||||
new ASCII<u8"escape\0">;
|
||||
// CHECK{LITERAL}: ASCII<{u8"escape\r\n"}>
|
||||
new ASCII<u8"escape\r\n">;
|
||||
// CHECK{LITERAL}: ASCII<{{229, 165, 189, 239, 191, 189, 0}}>
|
||||
new ASCII<u8"\u597d\ufffd">;
|
||||
// CHECK{LITERAL}: ASCII<{u8"print more characters as string, no uplimit"}>
|
||||
new ASCII<u8"print more characters as string, no uplimit">;
|
||||
}
|
||||
|
||||
void utf16() {
|
||||
// CHECK{LITERAL}: ASCII<{u""}>
|
||||
new ASCII<u"">;
|
||||
// CHECK{LITERAL}: ASCII<{u"\\`~!@#$%^&*()_+-={}[]|\'\";:,.<>?/"}>
|
||||
new ASCII<uR"(\`~!@#$%^&*()_+-={}[]|'";:,.<>?/)">;
|
||||
// CHECK{LITERAL}: ASCII<{{101, 115, 99, 97, 112, 101, 0, 0}}>
|
||||
new ASCII<u"escape\0">;
|
||||
// CHECK{LITERAL}: ASCII<{u"escape\r\n"}>
|
||||
new ASCII<u"escape\r\n">;
|
||||
// CHECK{LITERAL}: ASCII<{{22909, 65533, 0}}>
|
||||
new ASCII<u"\u597d\ufffd">;
|
||||
// CHECK{LITERAL}: ASCII<{u"print more characters as string, no uplimit"}>
|
||||
new ASCII<u"print more characters as string, no uplimit">;
|
||||
}
|
||||
|
||||
void utf32() {
|
||||
// CHECK{LITERAL}: ASCII<{U""}>
|
||||
new ASCII<U"">;
|
||||
// CHECK{LITERAL}: ASCII<{U"\\`~!@#$%^&*()_+-={}[]|\'\";:,.<>?/"}>
|
||||
new ASCII<UR"(\`~!@#$%^&*()_+-={}[]|'";:,.<>?/)">;
|
||||
// CHECK{LITERAL}: ASCII<{{101, 115, 99, 97, 112, 101, 0, 0}}>
|
||||
new ASCII<U"escape\0">;
|
||||
// CHECK{LITERAL}: ASCII<{U"escape\r\n"}>
|
||||
new ASCII<U"escape\r\n">;
|
||||
// CHECK{LITERAL}: ASCII<{{22909, 131358, 0}}>
|
||||
new ASCII<U"\u597d\U0002011E">;
|
||||
// CHECK{LITERAL}: ASCII<{U"print more characters as string, no uplimit"}>
|
||||
new ASCII<U"print more characters as string, no uplimit">;
|
||||
}
|
|
@ -80,3 +80,34 @@ TEST(TypePrinter, ParamsUglified) {
|
|||
varDecl(hasType(qualType().bind("id"))),
|
||||
"const f<Tp &> *", Clean));
|
||||
}
|
||||
|
||||
TEST(TypePrinter, TemplateIdWithNTTP) {
|
||||
constexpr char Code[] = R"cpp(
|
||||
template <int N>
|
||||
struct Str {
|
||||
constexpr Str(char const (&s)[N]) { __builtin_memcpy(value, s, N); }
|
||||
char value[N];
|
||||
};
|
||||
template <Str> class ASCII {};
|
||||
|
||||
ASCII<"this nontype template argument is too long to print"> x;
|
||||
)cpp";
|
||||
auto Matcher = classTemplateSpecializationDecl(
|
||||
hasName("ASCII"), has(cxxConstructorDecl(
|
||||
isMoveConstructor(),
|
||||
has(parmVarDecl(hasType(qualType().bind("id")))))));
|
||||
|
||||
ASSERT_TRUE(PrintedTypeMatches(
|
||||
Code, {"-std=c++20"}, Matcher,
|
||||
R"(ASCII<{"this nontype template argument is [...]"}> &&)",
|
||||
[](PrintingPolicy &Policy) {
|
||||
Policy.EntireContentsOfLargeArray = false;
|
||||
}));
|
||||
|
||||
ASSERT_TRUE(PrintedTypeMatches(
|
||||
Code, {"-std=c++20"}, Matcher,
|
||||
R"(ASCII<{"this nontype template argument is too long to print"}> &&)",
|
||||
[](PrintingPolicy &Policy) {
|
||||
Policy.EntireContentsOfLargeArray = true;
|
||||
}));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue