[clang] 44eee65 - [AST] Print NTTP args as string-literals when possible
Zhihao Yuan via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 1 17:37:01 PST 2022
Author: Zhihao Yuan
Date: 2022-03-01T19:34:27-06:00
New Revision: 44eee659f1c530a684fa2e57a983d962b5980620
URL: https://github.com/llvm/llvm-project/commit/44eee659f1c530a684fa2e57a983d962b5980620
DIFF: https://github.com/llvm/llvm-project/commit/44eee659f1c530a684fa2e57a983d962b5980620.diff
LOG: [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
Added:
clang/test/SemaCXX/cxx2a-nttp-printing.cpp
clang/test/SemaTemplate/temp_arg_string_printing.cpp
Modified:
clang/include/clang/AST/DeclTemplate.h
clang/include/clang/AST/PrettyPrinter.h
clang/include/clang/Basic/CharInfo.h
clang/lib/AST/APValue.cpp
clang/lib/AST/ASTDiagnostic.cpp
clang/lib/AST/DeclTemplate.cpp
clang/lib/AST/Expr.cpp
clang/lib/AST/StmtPrinter.cpp
clang/lib/AST/TemplateBase.cpp
clang/lib/Sema/Sema.cpp
clang/unittests/AST/TypePrinterTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 319e605a8a1c5..0459ee8fb6164 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3313,10 +3313,12 @@ class TemplateParamObjectDecl : public ValueDecl,
/// 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; }
diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h
index fd40328d8dcf7..54cb57227f7a0 100644
--- a/clang/include/clang/AST/PrettyPrinter.h
+++ b/clang/include/clang/AST/PrettyPrinter.h
@@ -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;
};
diff --git a/clang/include/clang/Basic/CharInfo.h b/clang/include/clang/Basic/CharInfo.h
index c751b6a005e28..7d41193835089 100644
--- a/clang/include/clang/Basic/CharInfo.h
+++ b/clang/include/clang/Basic/CharInfo.h
@@ -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.
///
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index ef333c7711663..9a09da774a469 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -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);
}
}
diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index 724ede272fbfa..9f5e5ef1e96e6 100644
--- a/clang/lib/AST/ASTDiagnostic.cpp
+++ b/clang/lib/AST/ASTDiagnostic.cpp
@@ -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);
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index d9ff3517a589c..3b4ed3107e5a8 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -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());
}
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 45e94847caee8..e7cf33ef512ce 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -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
diff erent 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 << '"';
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index a6aa9fe45b027..1cee3a1b4650c 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -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())
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index 619ce42f9dd1d..3418401517c84 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -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;
}
}
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index db3eda622639f..f1dc02d0b6dd9 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -114,6 +114,9 @@ PrintingPolicy Sema::getPrintingPolicy(const ASTContext &Context,
}
}
+ // Shorten the data output if needed
+ Policy.EntireContentsOfLargeArray = false;
+
return Policy;
}
diff --git a/clang/test/SemaCXX/cxx2a-nttp-printing.cpp b/clang/test/SemaCXX/cxx2a-nttp-printing.cpp
new file mode 100644
index 0000000000000..f1441551e408c
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2a-nttp-printing.cpp
@@ -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??!"}>'}}
+}
diff --git a/clang/test/SemaTemplate/temp_arg_string_printing.cpp b/clang/test/SemaTemplate/temp_arg_string_printing.cpp
new file mode 100644
index 0000000000000..5b73ef7edb7b6
--- /dev/null
+++ b/clang/test/SemaTemplate/temp_arg_string_printing.cpp
@@ -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">;
+}
diff --git a/clang/unittests/AST/TypePrinterTest.cpp b/clang/unittests/AST/TypePrinterTest.cpp
index 8500d518d25fe..7b44b1ecece24 100644
--- a/clang/unittests/AST/TypePrinterTest.cpp
+++ b/clang/unittests/AST/TypePrinterTest.cpp
@@ -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;
+ }));
+}
More information about the cfe-commits
mailing list