diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -285,6 +285,10 @@ def err_unsupported_string_concat : Error< "unsupported non-standard concatenation of string literals">; +def warn_unevaluated_string_prefix : Warning< + "encoding prefix '%0' on an unevaluated string literal has no effect" + "%select{| and is incompatible with c++2c}1">, + InGroup>; def err_unevaluated_string_prefix : Error< "an unevaluated string literal cannot have an encoding prefix">; def err_unevaluated_string_udl : Error< diff --git a/clang/lib/Lex/LiteralSupport.cpp b/clang/lib/Lex/LiteralSupport.cpp --- a/clang/lib/Lex/LiteralSupport.cpp +++ b/clang/lib/Lex/LiteralSupport.cpp @@ -57,6 +57,26 @@ } } +static unsigned getEncodingPrefixLen(tok::TokenKind kind) { + switch (kind) { + default: + llvm_unreachable("Unknown token type!"); + case tok::char_constant: + case tok::string_literal: + return 0; + case tok::utf8_char_constant: + case tok::utf8_string_literal: + return 2; + case tok::wide_char_constant: + case tok::wide_string_literal: + case tok::utf16_char_constant: + case tok::utf16_string_literal: + case tok::utf32_char_constant: + case tok::utf32_string_literal: + return 1; + } +} + static CharSourceRange MakeCharSourceRange(const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, @@ -343,7 +363,9 @@ Diag(Diags, Features, Loc, ThisTokBegin, EscapeBegin, ThisTokBuf, diag::err_unevaluated_string_invalid_escape_sequence) << StringRef(EscapeBegin, ThisTokBuf - EscapeBegin); + HadError = true; } + return ResultChar; } @@ -1917,9 +1939,22 @@ // Remember if we see any wide or utf-8/16/32 strings. // Also check for illegal concatenations. if (isUnevaluated() && Tok.getKind() != tok::string_literal) { - if (Diags) - Diags->Report(Tok.getLocation(), diag::err_unevaluated_string_prefix); - hadError = true; + if (Diags) { + SourceLocation PrefixEndLoc = Lexer::AdvanceToTokenCharacter( + Tok.getLocation(), getEncodingPrefixLen(Tok.getKind()), SM, + Features); + CharSourceRange Range = + CharSourceRange::getCharRange({Tok.getLocation(), PrefixEndLoc}); + StringRef Prefix(SM.getCharacterData(Tok.getLocation()), + getEncodingPrefixLen(Tok.getKind())); + Diags->Report(Tok.getLocation(), + Features.CPlusPlus26 + ? diag::err_unevaluated_string_prefix + : diag::warn_unevaluated_string_prefix) + << Prefix << Features.CPlusPlus << FixItHint::CreateRemoval(Range); + } + if (Features.CPlusPlus26) + hadError = true; } else if (Tok.isNot(Kind) && Tok.isNot(tok::string_literal)) { if (isOrdinary()) { Kind = Tok.getKind(); diff --git a/clang/test/CXX/dcl.dcl/dcl.link/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.link/p2.cpp --- a/clang/test/CXX/dcl.dcl/dcl.link/p2.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.link/p2.cpp @@ -8,7 +8,7 @@ extern "C" plusplus { } -extern u8"C" {} // expected-error {{an unevaluated string literal cannot have an encoding prefix}} -extern L"C" {} // expected-error {{an unevaluated string literal cannot have an encoding prefix}} -extern u"C++" {} // expected-error {{an unevaluated string literal cannot have an encoding prefix}} -extern U"C" {} // expected-error {{an unevaluated string literal cannot have an encoding prefix}} +extern u8"C" {} // expected-warning {{encoding prefix 'u8' on an unevaluated string literal has no effect and is incompatible with c++2c}} +extern L"C" {} // expected-warning {{encoding prefix 'L' on an unevaluated string literal has no effect and is incompatible with c++2c}} +extern u"C++" {} // expected-warning {{encoding prefix 'u' on an unevaluated string literal has no effect and is incompatible with c++2c}} +extern U"C" {} // expected-warning {{encoding prefix 'U' on an unevaluated string literal has no effect and is incompatible with c++2c}} diff --git a/clang/test/CXX/dcl.dcl/p4-0x.cpp b/clang/test/CXX/dcl.dcl/p4-0x.cpp --- a/clang/test/CXX/dcl.dcl/p4-0x.cpp +++ b/clang/test/CXX/dcl.dcl/p4-0x.cpp @@ -18,7 +18,7 @@ static_assert(T(), ""); static_assert(U(), ""); // expected-error {{ambiguous}} -static_assert(false, L"\x14hi" // expected-error {{an unevaluated string literal cannot have an encoding prefix}} \ +static_assert(false, L"\x14hi" // expected-warning {{encoding prefix 'L' on an unevaluated string literal has no effect and is incompatible with c++2c}} \ // expected-error {{invalid escape sequence '\x14' in an unevaluated string literal}} "!" R"x(")x"); diff --git a/clang/test/FixIt/unevaluated-strings.cpp b/clang/test/FixIt/unevaluated-strings.cpp new file mode 100644 --- /dev/null +++ b/clang/test/FixIt/unevaluated-strings.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -verify -std=c++2c %s +// RUN: cp %s %t +// RUN: not %clang_cc1 -x c++ -std=c++2c -fixit %t +// RUN: %clang_cc1 -x c++ -std=c++2c %t +// RUN: not %clang_cc1 -std=c++2c -x c++ -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +static_assert(true, L""); // expected-error{{an unevaluated string literal cannot have an encoding prefix}} +// CHECK: fix-it:{{.*}}:{7:21-7:22} + +static_assert(true, u8""); // expected-error{{an unevaluated string literal cannot have an encoding prefix}} +// CHECK: fix-it:{{.*}}:{10:21-10:23} + +static_assert(true, u""); // expected-error{{an unevaluated string literal cannot have an encoding prefix}} +// CHECK: fix-it:{{.*}}:{13:21-13:22} + +static_assert(true, U""); // expected-error{{an unevaluated string literal cannot have an encoding prefix}} +// CHECK: fix-it:{{.*}}:{16:21-16:22} diff --git a/clang/test/SemaCXX/static-assert.cpp b/clang/test/SemaCXX/static-assert.cpp --- a/clang/test/SemaCXX/static-assert.cpp +++ b/clang/test/SemaCXX/static-assert.cpp @@ -29,13 +29,19 @@ S s1; // expected-note {{in instantiation of template class 'S' requested here}} S s2; -static_assert(false, L"\xFFFFFFFF"); // expected-error {{an unevaluated string literal cannot have an encoding prefix}} \ - // expected-error {{invalid escape sequence '\xFFFFFFFF' in an unevaluated string literal}} -static_assert(false, u"\U000317FF"); // expected-error {{an unevaluated string literal cannot have an encoding prefix}} -// FIXME: render this as u8"\u03A9" -static_assert(false, u8"Ω"); // expected-error {{an unevaluated string literal cannot have an encoding prefix}} -static_assert(false, L"\u1234"); // expected-error {{an unevaluated string literal cannot have an encoding prefix}} -static_assert(false, L"\x1ff" // expected-error {{an unevaluated string literal cannot have an encoding prefix}} \ +static_assert(false, L"\xFFFFFFFF"); // expected-warning {{encoding prefix 'L' on an unevaluated string literal has no effect and is incompatible with c++2c}} \ + // expected-error {{invalid escape sequence '\xFFFFFFFF' in an unevaluated string literal}} \ + // expected-error {{hex escape sequence out of range}} +static_assert(false, u"\U000317FF"); // expected-warning {{encoding prefix 'u' on an unevaluated string literal has no effect and is incompatible with c++2c}} \ + // expected-error {{static assertion failed}} + +static_assert(false, u8"Ω"); // expected-warning {{encoding prefix 'u8' on an unevaluated string literal has no effect and is incompatible with c++2c}} \ + // expected-error {{static assertion failed: Ω}} +static_assert(false, L"\u1234"); // expected-warning {{encoding prefix 'L' on an unevaluated string literal has no effect and is incompatible with c++2c}} \ + // expected-error {{static assertion failed: ሴ}} + +static_assert(false, L"\x1ff" // expected-warning {{encoding prefix 'L' on an unevaluated string literal has no effect and is incompatible with c++2c}} \ + // expected-error {{hex escape sequence out of range}} \ // expected-error {{invalid escape sequence '\x1ff' in an unevaluated string literal}} "0\x123" // expected-error {{invalid escape sequence '\x123' in an unevaluated string literal}} "fx\xfffff" // expected-error {{invalid escape sequence '\xfffff' in an unevaluated string literal}}