Skip to content

Commit

Permalink
[Clang] Backport static_assert messages fixes
Browse files Browse the repository at this point in the history
* 4d494e7: Handle static_assert messages with an expression started by a literal
* 49e0495 Produce a warning instead of an error in unevaluated strings before C++26

Emiting an error on unexpected encoding prefix - which was allowed before C++26 -
caused build errors for a few users.
This downgrade the error to a warning on older language modes and C
  • Loading branch information
cor3ntin committed Aug 10, 2023
1 parent f8468c3 commit 51e2839
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 18 deletions.
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticLexKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ def ext_ms_reserved_user_defined_literal : ExtWarn<
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<DiagGroup<"invalid-unevaluated-string">>;
def err_unevaluated_string_prefix : Error<
"an unevaluated string literal cannot have an encoding prefix">;
def err_unevaluated_string_udl : Error<
Expand Down
41 changes: 38 additions & 3 deletions clang/lib/Lex/LiteralSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,26 @@ static unsigned getCharWidth(tok::TokenKind kind, const TargetInfo &Target) {
}
}

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,
Expand Down Expand Up @@ -343,7 +363,9 @@ static unsigned ProcessCharEscape(const char *ThisTokBegin,
Diag(Diags, Features, Loc, ThisTokBegin, EscapeBegin, ThisTokBuf,
diag::err_unevaluated_string_invalid_escape_sequence)
<< StringRef(EscapeBegin, ThisTokBuf - EscapeBegin);
HadError = true;
}

return ResultChar;
}

Expand Down Expand Up @@ -1917,9 +1939,22 @@ void StringLiteralParser::init(ArrayRef<Token> StringToks){
// 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();
Expand Down
19 changes: 16 additions & 3 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1016,10 +1016,23 @@ Decl *Parser::ParseStaticAssertDeclaration(SourceLocation &DeclEnd) {
return nullptr;
}

if (isTokenStringLiteral())
AssertMessage = ParseUnevaluatedStringLiteralExpression();
else if (getLangOpts().CPlusPlus26)
bool ParseAsExpression = false;
if (getLangOpts().CPlusPlus26) {
for (unsigned I = 0;; ++I) {
const Token &T = GetLookAheadToken(I);
if (T.is(tok::r_paren))
break;
if (!tok::isStringLiteral(Tok.getKind())) {
ParseAsExpression = true;
break;
}
}
}

if (ParseAsExpression)
AssertMessage = ParseConstantExpressionInExprEvalContext();
else if (tok::isStringLiteral(Tok.getKind()))
AssertMessage = ParseUnevaluatedStringLiteralExpression();
else {
Diag(Tok, diag::err_expected_string_literal)
<< /*Source='static_assert'*/ 1;
Expand Down
8 changes: 4 additions & 4 deletions clang/test/CXX/dcl.dcl/dcl.link/p2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ extern "C" {
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}}
2 changes: 1 addition & 1 deletion clang/test/CXX/dcl.dcl/p4-0x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ static_assert(S(false), "not so fast"); // expected-error {{not so fast}}
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");
17 changes: 17 additions & 0 deletions clang/test/FixIt/unevaluated-strings.cpp
Original file line number Diff line number Diff line change
@@ -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}
17 changes: 17 additions & 0 deletions clang/test/Sema/static-assert.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,20 @@ _Static_assert(__builtin_strlen("1"), ""); // ext-warning {{'_Static_assert' is
// __builtin_strlen(literal) is considered an integer constant expression
// and doesn't cause a pedantic warning
#endif


_Static_assert(0, L"\xFFFFFFFF"); // expected-warning {{encoding prefix 'L' on an unevaluated string literal has no effect}} \
// expected-error {{invalid escape sequence '\xFFFFFFFF' in an unevaluated string literal}} \
// expected-error {{hex escape sequence out of range}} \
// ext-warning {{'_Static_assert' is a C11 extension}}
_Static_assert(0, L"\u1234"); // expected-warning {{encoding prefix 'L' on an unevaluated string literal has no effect}} \
// expected-error {{static assertion failed: ሴ}} \
// ext-warning {{'_Static_assert' is a C11 extension}}

_Static_assert(0, L"\x1ff" // expected-warning {{encoding prefix 'L' on an unevaluated string literal has no effect}} \
// expected-error {{hex escape sequence out of range}} \
// expected-error {{invalid escape sequence '\x1ff' in an unevaluated string literal}} \
// ext-warning {{'_Static_assert' is a C11 extension}}
"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}}
"goop");
20 changes: 13 additions & 7 deletions clang/test/SemaCXX/static-assert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,19 @@ template<typename T> struct S {
S<char> s1; // expected-note {{in instantiation of template class 'S<char>' requested here}}
S<int> 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}}
Expand Down

0 comments on commit 51e2839

Please sign in to comment.