diff options
author | Takuya Shimizu <shimizu2486@gmail.com> | 2023-10-04 14:09:06 +0900 |
---|---|---|
committer | Takuya Shimizu <shimizu2486@gmail.com> | 2023-10-04 14:09:06 +0900 |
commit | 2176c5e510e3bfcbc75afb13e78d287141f239a7 (patch) | |
tree | 0c118b2217f0e8c240d373ddd44e5ac3a355d8ea /clang/lib | |
parent | 8641cdf397d86f33ac45e4c691ca4f843c359370 (diff) | |
download | llvm-2176c5e510e3bfcbc75afb13e78d287141f239a7.zip llvm-2176c5e510e3bfcbc75afb13e78d287141f239a7.tar.gz llvm-2176c5e510e3bfcbc75afb13e78d287141f239a7.tar.bz2 |
[Clang][Sema] Fix display of characters on static assertion failure
This patch fixes the display of characters appearing in LHS or RHS of == expression in notes to static assertion failure.
This applies C-style escape if the printed character is a special character. This also adds a numerical value displayed next to the character representation.
This also tries to print multi-byte characters if the user-provided expression is multi-byte char type.
Reviewed By: cor3ntin
Differential Revision: https://reviews.llvm.org/D155610
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Basic/Diagnostic.cpp | 11 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 107 |
2 files changed, 105 insertions, 13 deletions
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index 7a54d27..0208ccc 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -800,9 +800,10 @@ FormatDiagnostic(SmallVectorImpl<char> &OutStr) const { FormatDiagnostic(Diag.begin(), Diag.end(), OutStr); } -/// pushEscapedString - Append Str to the diagnostic buffer, +/// EscapeStringForDiagnostic - Append Str to the diagnostic buffer, /// escaping non-printable characters and ill-formed code unit sequences. -static void pushEscapedString(StringRef Str, SmallVectorImpl<char> &OutStr) { +void clang::EscapeStringForDiagnostic(StringRef Str, + SmallVectorImpl<char> &OutStr) { OutStr.reserve(OutStr.size() + Str.size()); auto *Begin = reinterpret_cast<const unsigned char *>(Str.data()); llvm::raw_svector_ostream OutStream(OutStr); @@ -854,7 +855,7 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, StringRef(DiagStr, DiagEnd - DiagStr).equals("%0") && getArgKind(0) == DiagnosticsEngine::ak_std_string) { const std::string &S = getArgStdStr(0); - pushEscapedString(S, OutStr); + EscapeStringForDiagnostic(S, OutStr); return; } @@ -961,7 +962,7 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, case DiagnosticsEngine::ak_std_string: { const std::string &S = getArgStdStr(ArgNo); assert(ModifierLen == 0 && "No modifiers for strings yet"); - pushEscapedString(S, OutStr); + EscapeStringForDiagnostic(S, OutStr); break; } case DiagnosticsEngine::ak_c_string: { @@ -971,7 +972,7 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, // Don't crash if get passed a null pointer by accident. if (!S) S = "(null)"; - pushEscapedString(S, OutStr); + EscapeStringForDiagnostic(S, OutStr); break; } // ---- INTEGERS ---- diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 7ebb38e..f9c010b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -49,6 +49,7 @@ #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/SaveAndRestore.h" #include <map> #include <optional> @@ -17026,10 +17027,74 @@ Decl *Sema::ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc, AssertMessageExpr, RParenLoc, false); } +static void WriteCharTypePrefix(BuiltinType::Kind BTK, llvm::raw_ostream &OS) { + switch (BTK) { + case BuiltinType::Char_S: + case BuiltinType::Char_U: + break; + case BuiltinType::Char8: + OS << "u8"; + break; + case BuiltinType::Char16: + OS << 'u'; + break; + case BuiltinType::Char32: + OS << 'U'; + break; + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: + OS << 'L'; + break; + default: + llvm_unreachable("Non-character type"); + } +} + +/// Convert character's value, interpreted as a code unit, to a string. +/// The value needs to be zero-extended to 32-bits. +/// FIXME: This assumes Unicode literal encodings +static void WriteCharValueForDiagnostic(uint32_t Value, const BuiltinType *BTy, + unsigned TyWidth, + SmallVectorImpl<char> &Str) { + char Arr[UNI_MAX_UTF8_BYTES_PER_CODE_POINT]; + char *Ptr = Arr; + BuiltinType::Kind K = BTy->getKind(); + llvm::raw_svector_ostream OS(Str); + + // This should catch Char_S, Char_U, Char8, and use of escaped characters in + // other types. + if (K == BuiltinType::Char_S || K == BuiltinType::Char_U || + K == BuiltinType::Char8 || Value <= 0x7F) { + StringRef Escaped = escapeCStyle<EscapeChar::Single>(Value); + if (!Escaped.empty()) + EscapeStringForDiagnostic(Escaped, Str); + else + OS << static_cast<char>(Value); + return; + } + + switch (K) { + case BuiltinType::Char16: + case BuiltinType::Char32: + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: { + if (llvm::ConvertCodePointToUTF8(Value, Ptr)) + EscapeStringForDiagnostic(StringRef(Arr, Ptr - Arr), Str); + else + OS << "\\x" + << llvm::format_hex_no_prefix(Value, TyWidth / 4, /*Upper=*/true); + break; + } + default: + llvm_unreachable("Non-character type is passed"); + } +} + /// Convert \V to a string we can present to the user in a diagnostic /// \T is the type of the expression that has been evaluated into \V static bool ConvertAPValueToString(const APValue &V, QualType T, - SmallVectorImpl<char> &Str) { + SmallVectorImpl<char> &Str, + ASTContext &Context) { if (!V.hasValue()) return false; @@ -17044,13 +17109,38 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, "Bool type, but value is not 0 or 1"); llvm::raw_svector_ostream OS(Str); OS << (BoolValue ? "true" : "false"); - } else if (T->isCharType()) { + } else { + llvm::raw_svector_ostream OS(Str); // Same is true for chars. - Str.push_back('\''); - Str.push_back(V.getInt().getExtValue()); - Str.push_back('\''); - } else + // We want to print the character representation for textual types + const auto *BTy = T->getAs<BuiltinType>(); + if (BTy) { + switch (BTy->getKind()) { + case BuiltinType::Char_S: + case BuiltinType::Char_U: + case BuiltinType::Char8: + case BuiltinType::Char16: + case BuiltinType::Char32: + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: { + unsigned TyWidth = Context.getIntWidth(T); + assert(8 <= TyWidth && TyWidth <= 32 && "Unexpected integer width"); + uint32_t CodeUnit = static_cast<uint32_t>(V.getInt().getZExtValue()); + WriteCharTypePrefix(BTy->getKind(), OS); + OS << '\''; + WriteCharValueForDiagnostic(CodeUnit, BTy, TyWidth, Str); + OS << "' (0x" + << llvm::format_hex_no_prefix(CodeUnit, /*Width=*/2, + /*Upper=*/true) + << ", " << V.getInt() << ')'; + return true; + } + default: + break; + } + } V.getInt().toString(Str); + } break; @@ -17147,8 +17237,9 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { Side->EvaluateAsRValue(DiagSide[I].Result, Context, true); - DiagSide[I].Print = ConvertAPValueToString( - DiagSide[I].Result.Val, Side->getType(), DiagSide[I].ValueString); + DiagSide[I].Print = + ConvertAPValueToString(DiagSide[I].Result.Val, Side->getType(), + DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) |