Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ C Language Changes

char buf1[3] = "foo"; // -Wunterminated-string-initialization
char buf2[3] = "flarp"; // -Wexcess-initializers
char buf3[3] = "fo\0"; // This is fine, no warning.

This diagnostic can be suppressed by adding the new ``nonstring`` attribute
to the field or variable being initialized. #GH137705
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,12 @@ static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT,
diag::ext_initializer_string_for_char_array_too_long)
<< Str->getSourceRange();
else if (StrLength - 1 == ArrayLen) {
// If the string literal is null-terminated explicitly, e.g., `char a[4] =
// "ABC\0"`, there should be no warning:
if (const auto *SL = dyn_cast<StringLiteral>(Str->IgnoreParens());
SL && SL->getLength() > 0 &&
SL->getCodeUnit(SL->getLength() - 1) == 0)
return;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the early return suppresses warn_initializer_string_for_char_array_too_long_for_cpp warnings?

// If the entity being initialized has the nonstring attribute, then
// silence the "missing nonstring" diagnostic. If there's no entity,
// check whether we're initializing an array of arrays; if so, walk the
Expand Down
53 changes: 53 additions & 0 deletions clang/test/Sema/attr-nonstring_safe.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wunterminated-string-initialization %s -x c
// RUN: %clang_cc1 -fsyntax-only -verify=cxx,expected -Wunterminated-string-initialization %s -x c++


#ifdef __cplusplus
// C++ is stricter so the following cases should be warned about:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want the same test cases between C and C++, the only thing that should be different is the expected diagnostic comments. e.g.,

char foo3[3] = "fo\0"; // cxx-error {{initializer-string for char array is too long, array size is 3 but initializer has size 4 (including the null terminating character)}}

is fine in C because the diagnostic will only be checked in C++ mode.

The only code that should be in the #if would be things like the typedefs for C.


char foo3[3] = "fo\0"; // cxx-error {{initializer-string for char array is too long, array size is 3 but initializer has size 4 (including the null terminating character)}}
char foo1[1] = "\0"; // cxx-error {{initializer-string for char array is too long, array size is 1 but initializer has size 2 (including the null terminating character)}}

struct S {
char buf[3];
char fub[3];
} s = { "ba\0", "bo\0" }; // cxx-error 2{{initializer-string for char array is too long, array size is 3 but initializer has size 4 (including the null terminating character)}}

signed char scfoo[3] = "fo\0"; // cxx-error {{initializer-string for char array is too long, array size is 3 but initializer has size 4 (including the null terminating character)}}
unsigned char ucfoo[3] = "fo\0"; // cxx-error {{initializer-string for char array is too long, array size is 3 but initializer has size 4 (including the null terminating character)}}
wchar_t wcfoo[3] = L"fo\0"; // cxx-error {{initializer-string for char array is too long, array size is 3 but initializer has size 4 (including the null terminating character)}}
char16_t c16foo[3] = u"fo\0"; // cxx-error {{initializer-string for char array is too long, array size is 3 but initializer has size 4 (including the null terminating character)}}
char32_t c32foo[3] = U"fo\0"; // cxx-error {{initializer-string for char array is too long, array size is 3 but initializer has size 4 (including the null terminating character)}}
#else

// In C, the following examples are fine:
typedef unsigned short char16_t;
typedef unsigned int char32_t;
typedef __WCHAR_TYPE__ wchar_t;

char foo3[3] = "fo\0";
char foo1[1] = "\0";

struct S {
char buf[3];
char fub[3];
} s = { "ba\0", "bo\0" };

// Test different encodings:
signed char scfoo[3] = "fo\0";
unsigned char ucfoo[3] = "fo\0";
wchar_t wcfoo[3] = L"fo\0";
char16_t c16foo[3] = u"fo\0";
char32_t c32foo[3] = U"fo\0";

// Test list initializer:
signed char scfoo_lst[3] = {'f', 'o', '\0'};
unsigned char ucfoo_lst[3] = {'f', 'o', '\0'};
wchar_t wcfoo_lst[3] = {L'f', L'o', L'\0'};
char16_t c16foo_lst[3] = {u'f', u'o', u'\0'};
char32_t c32foo_lst[3] = {U'f', U'o', U'\0'};

// Declaring an array of size 0 is invalid by C standard but compilers
// may allow it:
char a[0] = ""; // expected-warning {{initializer-string for character array is too long, array size is 0 but initializer has size 1 (including the null terminating character); did you mean to use the 'nonstring' attribute?}}
#endif