Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -132,6 +132,44 @@ of a base class is not called in the constructor of its derived class. - Clang no longer emits ``-Wmissing-variable-declarations`` for variables declared with the ``register`` storage class. +- Clang now warns on unused variables declared and initialized in condition + expressions. + (`#61681: `_) +- When describing the failure of static assertion of `==` expression, clang prints the integer + representation of the value as well as its character representation when + the user-provided expression is of character type. If the character is + non-printable, clang now shows the escpaed character. + Clang also prints multi-byte characters if the user-provided expression + is of multi-byte character type. + + *Example Code*: + + .. code-block:: c++ + + static_assert("A\n"[1] == U'🌍'); + + *BEFORE*: + + .. code-block:: text + + source:1:15: error: static assertion failed due to requirement '"A\n"[1] == U'\U0001f30d'' + 1 | static_assert("A\n"[1] == U'🌍'); + | ^~~~~~~~~~~~~~~~~ + source:1:24: note: expression evaluates to '' + ' == 127757' + 1 | static_assert("A\n"[1] == U'🌍'); + | ~~~~~~~~~^~~~~~~~ + + *AFTER*: + + .. code-block:: text + + source:1:15: error: static assertion failed due to requirement '"A\n"[1] == U'\U0001f30d'' + 1 | static_assert("A\n"[1] == U'🌍'); + | ^~~~~~~~~~~~~~~~~ + source:1:24: note: expression evaluates to ''\n' (0x0A, 10) == '🌍' (0x1F30D, 127757)' + 1 | static_assert("A\n"[1] == U'🌍'); + | ~~~~~~~~~^~~~~~~~ Bug Fixes in This Version ------------------------- Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -46,6 +46,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 #include @@ -16863,10 +16864,52 @@ AssertMessageExpr, RParenLoc, false); } +/// Convert character's code unit value to a string. +/// The code point needs to be zero-extended to 32-bits. +static void WriteCharValueForDiagnostic(uint32_t Value, const BuiltinType *BTy, + unsigned TyWidth, + llvm::raw_ostream &OS) { + char Arr[UNI_MAX_UTF8_BYTES_PER_CODE_POINT]; + char *Ptr = Arr; + BuiltinType::Kind K = BTy->getKind(); + + // 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(Value); + if (!Escaped.empty()) + OS << Escaped; + else + OS << static_cast(Value); + return; + } + + switch (K) { + case BuiltinType::Char16: + case BuiltinType::Char32: + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: { + if (llvm::ConvertCodePointToUTF8(Value, Ptr)) { + for (char *I = Arr; I != Ptr; ++I) + OS << *I; + } else { + // FIXME: This assumes Unicode literal encodings + 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 &Str) { + SmallVectorImpl &Str, + ASTContext &Context) { if (!V.hasValue()) return false; @@ -16881,13 +16924,37 @@ "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(); + 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(V.getInt().getZExtValue()); + OS << '\''; + WriteCharValueForDiagnostic(CodeUnit, BTy, TyWidth, OS); + OS << "' (0x" + << llvm::format_hex_no_prefix(CodeUnit, /*Width=*/2, + /*Upper=*/true) + << ", " << V.getInt() << ')'; + return true; + } + default: + break; + } + } V.getInt().toString(Str); + } break; @@ -16984,8 +17051,9 @@ 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) Index: clang/test/Lexer/cxx1z-trigraphs.cpp =================================================================== --- clang/test/Lexer/cxx1z-trigraphs.cpp +++ clang/test/Lexer/cxx1z-trigraphs.cpp @@ -21,7 +21,7 @@ #if !ENABLED_TRIGRAPHS // expected-error@11 {{}} expected-warning@11 {{trigraph ignored}} -// expected-error@13 {{failed}} expected-warning@13 {{trigraph ignored}} expected-note@13 {{evaluates to ''?' == '#''}} +// expected-error@13 {{failed}} expected-warning@13 {{trigraph ignored}} expected-note@13 {{evaluates to ''?' (0x3F, 63) == '#' (0x23, 35)'}} // expected-error@16 {{}} // expected-error@20 {{}} #else Index: clang/test/SemaCXX/static-assert-cxx26.cpp =================================================================== --- clang/test/SemaCXX/static-assert-cxx26.cpp +++ clang/test/SemaCXX/static-assert-cxx26.cpp @@ -298,3 +298,12 @@ Bad b; // expected-note {{in instantiation}} } + +namespace EscapeInDiagnostic { +static_assert('\u{9}' == (char)1, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''\t' (0x09, 9) == '' (0x01, 1)'}} +static_assert((char8_t)-128 == (char8_t)-123, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''<80>' (0x80, 128) == '<85>' (0x85, 133)'}} +static_assert((char16_t)0xFEFF == (char16_t)0xDB93, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''' (0xFEFF, 65279) == '\xDB93' (0xDB93, 56211)'}} +} Index: clang/test/SemaCXX/static-assert.cpp =================================================================== --- clang/test/SemaCXX/static-assert.cpp +++ clang/test/SemaCXX/static-assert.cpp @@ -268,7 +268,31 @@ return 'c'; } static_assert(getChar() == 'a', ""); // expected-error {{failed}} \ - // expected-note {{evaluates to ''c' == 'a''}} + // expected-note {{evaluates to ''c' (0x63, 99) == 'a' (0x61, 97)'}} + static_assert((char)9 == '\x61', ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''\t' (0x09, 9) == 'a' (0x61, 97)'}} + static_assert((char)10 == '\0', ""); // expected-error {{failed}} \ + // expected-note {{n' (0x0A, 10) == '' (0x00, 0)'}} + // The note above is intended to match "evaluates to '\n' (0x0A, 10) == '' (0x00, 0)'", but if we write it as it is, + // the "\n" cannot be consumed by the diagnostic consumer. + static_assert((signed char)10 == (char)-123, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to '10 == '<85>' (0x85, -123)'}} + static_assert((char)-4 == (unsigned char)-8, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''' (0xFC, -4) == 248'}} + static_assert((char)-128 == (char)-123, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''<80>' (0x80, -128) == '<85>' (0x85, -123)'}} + static_assert('\xA0' == (char)'\x20', ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''' (0xA0, -96) == ' ' (0x20, 32)'}} + static_assert((char16_t)L'ゆ' == L"C̵̭̯̠̎͌ͅť̺"[1], ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''ゆ' (0x3086, 12422) == '̵' (0x335, 821)'}} + static_assert(L"\/"[1] == u'\xFFFD', ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''/' (0xFF0F, 65295) == '�' (0xFFFD, 65533)'}} + static_assert(L"⚾"[0] == U'🌍', ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''⚾' (0x26BE, 9918) == '🌍' (0x1F30D, 127757)'}} + static_assert(U"\a"[0] == (wchar_t)9, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''\a' (0x07, 7) == '\t' (0x09, 9)'}} + static_assert(L"§"[0] == U'Ö', ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''§' (0xA7, 167) == 'Ö' (0xD6, 214)'}} /// Bools are printed as bools. constexpr bool invert(bool b) {