diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -66,6 +66,11 @@ C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- Attributes now expect unevaluated strings in attributes parameters that are string literals. + This is applied to both C++ standard attributes, and other attributes supported by Clang. + This completes the implementation of `P2361R6 Unevaluated Strings _` + + Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -56,7 +56,9 @@ "%select{in %1|for diagnostic message in static_assert|" "for optional message in 'availability' attribute|" "for %select{language name|source container name|USR}1 in " - "'external_source_symbol' attribute}0">; + "'external_source_symbol' attribute|" + "as argument of '%1' attribute}0">; + def err_invalid_string_udl : Error< "string literal with user-defined suffix cannot be used here">; def err_invalid_character_udl : Error< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2790,6 +2790,13 @@ /// clang accepts as an extension. void DiagnoseCXX11AttributeExtension(ParsedAttributes &Attrs); + ExprResult ParseUnevaluatedStringInAttribute(const IdentifierInfo &AttrName); + + bool + ParseAttributeArgumentList(const clang::IdentifierInfo &AttrName, + SmallVectorImpl &Exprs, + ParsedAttributeArgumentsProperties ArgsProperties); + /// Parses syntax-generic attribute arguments for attributes which are /// known to the implementation, and adds them to the given ParsedAttributes /// list with the given attribute syntax. Returns the number of arguments diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -24,6 +24,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/VersionTuple.h" +#include #include #include #include @@ -911,6 +912,20 @@ VecTy AttrList; }; +struct ParsedAttributeArgumentsProperties { + ParsedAttributeArgumentsProperties(uint32_t StringLiteralBits) + : StringLiterals(StringLiteralBits) {} + bool isStringLiteralArg(unsigned I) const { + // If the last bit is set, assume we have a variadic parameter + if (I >= StringLiterals.size()) + return StringLiterals.test(StringLiterals.size() - 1); + return StringLiterals.test(I); + } + +private: + std::bitset<32> StringLiterals; +}; + /// ParsedAttributes - A collection of parsed attributes. Currently /// we don't differentiate between the various attribute syntaxes, /// which is basically silly. diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -288,6 +288,16 @@ #undef CLANG_ATTR_IDENTIFIER_ARG_LIST } +/// Determine whether the given attribute has an identifier argument. +static ParsedAttributeArgumentsProperties +attributeStringLiteralListArg(const IdentifierInfo &II) { +#define CLANG_ATTR_STRING_LITERAL_ARG_LIST + return llvm::StringSwitch(normalizeAttrName(II.getName())) +#include "clang/Parse/AttrParserStringSwitches.inc" + .Default(0); +#undef CLANG_ATTR_STRING_LITERAL_ARG_LIST +} + /// Determine whether the given attribute has a variadic identifier argument. static bool attributeHasVariadicIdentifierArg(const IdentifierInfo &II) { #define CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST @@ -371,6 +381,82 @@ ScopeName, ScopeLoc, nullptr, 0, Form); } +ExprResult +Parser::ParseUnevaluatedStringInAttribute(const IdentifierInfo &AttrName) { + if (Tok.is(tok::l_paren)) { + BalancedDelimiterTracker Paren(*this, tok::l_paren); + Paren.consumeOpen(); + ExprResult Res = ParseUnevaluatedStringInAttribute(AttrName); + Paren.consumeClose(); + return Res; + } + if (!isTokenStringLiteral()) { + Diag(Tok.getLocation(), diag::err_expected_string_literal) + << /*in attribute...*/ 4 << AttrName.getName(); + return ExprError(); + } + return ParseUnevaluatedStringLiteralExpression(); +} + +bool Parser::ParseAttributeArgumentList( + const IdentifierInfo &AttrName, SmallVectorImpl &Exprs, + ParsedAttributeArgumentsProperties ArgsProperties) { + bool SawError = false; + unsigned Arg = 0; + while (true) { + ExprResult Expr; + if (ArgsProperties.isStringLiteralArg(Arg)) { + Expr = ParseUnevaluatedStringInAttribute(AttrName); + } else if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { + Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); + Expr = ParseBraceInitializer(); + } else { + Expr = ParseAssignmentExpression(); + } + Expr = Actions.CorrectDelayedTyposInExpr(Expr); + + if (Tok.is(tok::ellipsis)) + Expr = Actions.ActOnPackExpansion(Expr.get(), ConsumeToken()); + else if (Tok.is(tok::code_completion)) { + // There's nothing to suggest in here as we parsed a full expression. + // Instead fail and propogate the error since caller might have something + // the suggest, e.g. signature help in function call. Note that this is + // performed before pushing the \p Expr, so that signature help can report + // current argument correctly. + SawError = true; + cutOffParsing(); + break; + } + + if (Expr.isInvalid()) { + SawError = true; + break; + SkipUntil(tok::comma, tok::r_paren, StopBeforeMatch); + } else { + Exprs.push_back(Expr.get()); + } + + if (Tok.isNot(tok::comma)) + break; + // Move to the next argument, remember where the comma was. + Token Comma = Tok; + ConsumeToken(); + checkPotentialAngleBracketDelimiter(Comma); + Arg++; + } + + if (SawError) { + // Ensure typos get diagnosed when errors were encountered while parsing the + // expression list. + for (auto &E : Exprs) { + ExprResult Expr = Actions.CorrectDelayedTyposInExpr(E); + if (Expr.isUsable()) + E = Expr.get(); + } + } + return SawError; +} + unsigned Parser::ParseAttributeArgsCommon( IdentifierInfo *AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, @@ -463,9 +549,9 @@ : Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprVector ParsedExprs; - if (ParseExpressionList(ParsedExprs, llvm::function_ref(), - /*FailImmediatelyOnInvalidExpr=*/true, - /*EarlyTypoCorrection=*/true)) { + ParsedAttributeArgumentsProperties ArgProperties = + attributeStringLiteralListArg(*AttrName); + if (ParseAttributeArgumentList(*AttrName, ParsedExprs, ArgProperties)) { SkipUntil(tok::r_paren, StopAtSemi); return 0; } diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1026,7 +1026,6 @@ SkipMalformedDecl(); return nullptr; } - if (AssertMessage.isInvalid()) { SkipMalformedDecl(); return nullptr; diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -3494,9 +3494,9 @@ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); Expr = ParseBraceInitializer(); - } else + } else { Expr = ParseAssignmentExpression(); - + } if (EarlyTypoCorrection) Expr = Actions.CorrectDelayedTyposInExpr(Expr); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -349,7 +349,7 @@ if (ArgLocation) *ArgLocation = E->getBeginLoc(); - if (!Literal || !Literal->isOrdinary()) { + if (!Literal || (!Literal->isUnevaluated() && !Literal->isOrdinary())) { Diag(E->getBeginLoc(), diag::err_attribute_argument_type) << CI << AANT_ArgumentString; return false; @@ -381,6 +381,16 @@ // Now check for an actual string literal. Expr *ArgExpr = AL.getArgAsExpr(ArgNum); + const auto *Literal = dyn_cast(ArgExpr->IgnoreParenCasts()); + if (ArgLocation) + *ArgLocation = ArgExpr->getBeginLoc(); + + if (!Literal || (!Literal->isUnevaluated() && !Literal->isOrdinary())) { + Diag(ArgExpr->getBeginLoc(), diag::err_attribute_argument_type) + << AL << AANT_ArgumentString; + return false; + } + Str = Literal->getString(); return checkStringLiteralArgumentAttr(AL, ArgExpr, Str, ArgLocation); } diff --git a/clang/test/Parser/MicrosoftExtensions.cpp b/clang/test/Parser/MicrosoftExtensions.cpp --- a/clang/test/Parser/MicrosoftExtensions.cpp +++ b/clang/test/Parser/MicrosoftExtensions.cpp @@ -44,8 +44,8 @@ unsigned char Data4[8]; } GUID; -struct __declspec(uuid(L"00000000-0000-0000-1234-000000000047")) uuid_attr_bad1 { };// expected-error {{'uuid' attribute requires a string}} -struct __declspec(uuid(3)) uuid_attr_bad2 { };// expected-error {{'uuid' attribute requires a string}} +struct __declspec(uuid(L"00000000-0000-0000-1234-000000000047")) uuid_attr_bad1 { };// expected-error {{an unevaluated string literal cannot have an encoding prefix}} +struct __declspec(uuid(3)) uuid_attr_bad2 { };// expected-error {{expected string literal as argument of 'uuid' attribute}} struct __declspec(uuid("0000000-0000-0000-1234-0000500000047")) uuid_attr_bad3 { };// expected-error {{uuid attribute contains a malformed GUID}} struct __declspec(uuid("0000000-0000-0000-Z234-000000000047")) uuid_attr_bad4 { };// expected-error {{uuid attribute contains a malformed GUID}} struct __declspec(uuid("000000000000-0000-1234-000000000047")) uuid_attr_bad5 { };// expected-error {{uuid attribute contains a malformed GUID}} diff --git a/clang/test/Parser/c2x-attributes.c b/clang/test/Parser/c2x-attributes.c --- a/clang/test/Parser/c2x-attributes.c +++ b/clang/test/Parser/c2x-attributes.c @@ -16,7 +16,7 @@ // FIXME: this diagnostic can be improved. enum E3 [[]] { Seven }; // expected-error {{expected identifier or '('}} -[[deprecated([""])]] int WrongArgs; // expected-error {{expected expression}} +[[deprecated([""])]] int WrongArgs; // expected-error {{expected string literal as argument of 'deprecated' attribute}} [[,,,,,]] int Commas1; // ok [[,, maybe_unused]] int Commas2; // ok [[maybe_unused,,,]] int Commas3; // ok diff --git a/clang/test/Parser/cxx-attributes.cpp b/clang/test/Parser/cxx-attributes.cpp --- a/clang/test/Parser/cxx-attributes.cpp +++ b/clang/test/Parser/cxx-attributes.cpp @@ -41,7 +41,7 @@ pi = &i[0]; } -[[deprecated([""])]] int WrongArgs; // expected-error {{expected variable name or 'this' in lambda capture list}} +[[deprecated([""])]] int WrongArgs; // expected-error {{expected string literal as argument of 'deprecated' attribute}} [[,,,,,]] int Commas1; // ok [[,, maybe_unused]] int Commas2; // ok [[maybe_unused,,,]] int Commas3; // ok diff --git a/clang/test/Parser/cxx0x-attributes.cpp b/clang/test/Parser/cxx0x-attributes.cpp --- a/clang/test/Parser/cxx0x-attributes.cpp +++ b/clang/test/Parser/cxx0x-attributes.cpp @@ -94,7 +94,7 @@ [[]] [[]] alignas(16) [[]]{}; // expected-error {{an attribute list cannot appear here}} // The diagnostics here don't matter much, this just shouldn't crash: -class C final [[deprecated(l]] {}); // expected-error {{use of undeclared identifier}} expected-error {{expected ']'}} expected-error {{an attribute list cannot appear here}} expected-error {{expected unqualified-id}} +class C final [[deprecated(l]] {}); //expected-error {{expected string literal as argument of 'deprecated' attribute}} expected-error {{an attribute list cannot appear here}} expected-error {{expected unqualified-id}} class D final alignas ([l) {}]{}); // expected-error {{expected ',' or ']' in lambda capture list}} expected-error {{an attribute list cannot appear here}} [[]] struct with_init_declarators {} init_declarator; @@ -266,7 +266,7 @@ template void variadic_nttp() { void bar [[noreturn...]] (); // expected-error {{attribute 'noreturn' cannot be used as an attribute pack}} - void baz [[clang::no_sanitize(Is...)]] (); // expected-error {{attribute 'no_sanitize' does not support argument pack expansion}} + void baz [[clang::no_sanitize(Is...)]] (); // expected-error {{expected string literal as argument of 'no_sanitize' attribute}} void bor [[clang::annotate("A", "V" ...)]] (); // expected-error {{pack expansion does not contain any unexpanded parameter packs}} void bir [[clang::annotate("B", {1, 2, 3, 4})]] (); // expected-error {{'annotate' attribute requires parameter 1 to be a constant expression}} expected-note {{subexpression not valid in a constant expression}} void boo [[unknown::foo(Is...)]] (); // expected-warning {{unknown attribute 'foo' ignored}} @@ -445,3 +445,8 @@ ) { } }; + +namespace P2361 { +[[deprecated(L"abc")]] void a(); // expected-error{{an unevaluated string literal cannot have an encoding prefix}} +[[nodiscard("\123")]] int b(); // expected-error{{invalid escape sequence '\123' in an unevaluated string literal}} +} diff --git a/clang/test/Sema/MicrosoftExtensions.c b/clang/test/Sema/MicrosoftExtensions.c --- a/clang/test/Sema/MicrosoftExtensions.c +++ b/clang/test/Sema/MicrosoftExtensions.c @@ -123,7 +123,7 @@ #define MY_TEXT "This is also deprecated" __declspec(deprecated(MY_TEXT)) void Dfunc1( void ) {} // expected-note {{'Dfunc1' has been explicitly marked deprecated here}} -struct __declspec(deprecated(123)) DS2 {}; // expected-error {{'deprecated' attribute requires a string}} +struct __declspec(deprecated(123)) DS2 {}; // expected-error {{expected string literal as argument of 'deprecated' attribute}} void test( void ) { e1 = one; // expected-warning {{'e1' is deprecated: This is deprecated}} diff --git a/clang/test/Sema/annotate-type.c b/clang/test/Sema/annotate-type.c --- a/clang/test/Sema/annotate-type.c +++ b/clang/test/Sema/annotate-type.c @@ -20,10 +20,10 @@ [[clang::annotate_type("bar")]] int *z1; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} int *z2 [[clang::annotate_type("bar")]]; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} [[clang::annotate_type("bar")]]; // expected-error {{'annotate_type' attribute cannot be applied to a statement}} - int *[[clang::annotate_type(1)]] z3; // expected-error {{'annotate_type' attribute requires a string}} + int *[[clang::annotate_type(1)]] z3; // expected-error {{expected string literal as argument of 'annotate_type' attribute}} int *[[clang::annotate_type()]] z4; // expected-error {{'annotate_type' attribute takes at least 1 argument}} int *[[clang::annotate_type]] z5; // expected-error {{'annotate_type' attribute takes at least 1 argument}} - int *[[clang::annotate_type(some_function())]] z6; // expected-error {{'annotate_type' attribute requires a string}} + int *[[clang::annotate_type(some_function())]] z6; // expected-error {{expected string literal as argument of 'annotate_type' attribute}} int *[[clang::annotate_type("bar", some_function())]] z7; // expected-error {{'annotate_type' attribute requires parameter 1 to be a constant expression}} expected-note{{subexpression not valid in a constant expression}} int *[[clang::annotate_type("bar", z7)]] z8; // expected-error {{'annotate_type' attribute requires parameter 1 to be a constant expression}} expected-note{{subexpression not valid in a constant expression}} int *[[clang::annotate_type("bar", int)]] z9; // expected-error {{expected expression}} diff --git a/clang/test/Sema/annotate.c b/clang/test/Sema/annotate.c --- a/clang/test/Sema/annotate.c +++ b/clang/test/Sema/annotate.c @@ -3,8 +3,8 @@ void __attribute__((annotate("foo"))) foo(float *a) { __attribute__((annotate("bar"))) int x; [[clang::annotate("bar")]] int x2; - __attribute__((annotate(1))) int y; // expected-error {{'annotate' attribute requires a string}} - [[clang::annotate(1)]] int y2; // expected-error {{'annotate' attribute requires a string}} + __attribute__((annotate(1))) int y; // expected-error {{expected string literal as argument of 'annotate' attribute}} + [[clang::annotate(1)]] int y2; // expected-error {{expected string literal as argument of 'annotate' attribute}} __attribute__((annotate("bar", 1))) int z; [[clang::annotate("bar", 1)]] int z2; diff --git a/clang/test/Sema/attr-assume.c b/clang/test/Sema/attr-assume.c --- a/clang/test/Sema/attr-assume.c +++ b/clang/test/Sema/attr-assume.c @@ -1,8 +1,8 @@ // RUN: %clang_cc1 -triple i386-apple-darwin9 -fsyntax-only -verify %s -void f1(void) __attribute__((assume(3))); // expected-error {{'assume' attribute requires a string}} -void f2(void) __attribute__((assume(int))); // expected-error {{expected expression}} -void f3(void) __attribute__((assume(for))); // expected-error {{expected expression}} +void f1(void) __attribute__((assume(3))); // expected-error {{expected string literal as argument of 'assume' attribute}} +void f2(void) __attribute__((assume(int))); // expected-error {{expected string literal as argument of 'assume' attribute}} +void f3(void) __attribute__((assume(for))); // expected-error {{expected string literal as argument of 'assume' attribute}} void f4(void) __attribute__((assume("QQQQ"))); // expected-warning {{unknown assumption string 'QQQQ'; attribute is potentially ignored}} void f5(void) __attribute__((assume("omp_no_openmp"))); void f6(void) __attribute__((assume("omp_noopenmp"))); // expected-warning {{unknown assumption string 'omp_noopenmp' may be misspelled; attribute is potentially ignored, did you mean 'omp_no_openmp'?}} @@ -10,5 +10,5 @@ void f8(void) __attribute__((assume("omp_no_openmp1"))); // expected-warning {{unknown assumption string 'omp_no_openmp1' may be misspelled; attribute is potentially ignored, did you mean 'omp_no_openmp'?}} void f9(void) __attribute__((assume("omp_no_openmp", "omp_no_openmp"))); // expected-error {{'assume' attribute takes one argument}} -int g1 __attribute__((assume(0))); // expected-warning {{'assume' attribute only applies to functions and Objective-C methods}} +int g1 __attribute__((assume(0))); // expected-error {{expected string literal as argument of 'assume' attribute}} int g2 __attribute__((assume("omp_no_openmp"))); // expected-warning {{'assume' attribute only applies to functions and Objective-C methods}} diff --git a/clang/test/Sema/attr-btf_tag.c b/clang/test/Sema/attr-btf_tag.c --- a/clang/test/Sema/attr-btf_tag.c +++ b/clang/test/Sema/attr-btf_tag.c @@ -21,7 +21,7 @@ int g1 __tag1; int g2 __tag_no_arg; // expected-error {{'btf_decl_tag' attribute takes one argument}} int g3 __tag_2_arg; // expected-error {{'btf_decl_tag' attribute takes one argument}} -int i1 __invalid; // expected-error {{'btf_decl_tag' attribute requires a string}} +int i1 __invalid; // expected-error {{expected string literal as argument of 'btf_decl_tag' attribute}} enum e1 { E1 diff --git a/clang/test/Sema/attr-btf_type_tag.c b/clang/test/Sema/attr-btf_type_tag.c --- a/clang/test/Sema/attr-btf_type_tag.c +++ b/clang/test/Sema/attr-btf_type_tag.c @@ -8,7 +8,7 @@ #define __tag6 __attribute__((btf_type_tag("tag6"))) int __attribute__((btf_type_tag("tag1", "tag2"))) *invalid1; // expected-error {{'btf_type_tag' attribute takes one argument}} -int __attribute__((btf_type_tag(2))) *invalid2; // expected-error {{'btf_type_tag' attribute requires a string}} +int __attribute__((btf_type_tag(2))) *invalid2; // expected-error {{expected string literal as argument of 'btf_type_tag' attribute}} int * __tag1 __tag2 * __tag3 __tag4 * __tag5 __tag6 *g; diff --git a/clang/test/Sema/attr-capabilities.c b/clang/test/Sema/attr-capabilities.c --- a/clang/test/Sema/attr-capabilities.c +++ b/clang/test/Sema/attr-capabilities.c @@ -17,8 +17,8 @@ int Test4 __attribute__((try_acquire_capability("test4"))); // expected-error {{'try_acquire_capability' attribute only applies to functions}} int Test5 __attribute__((release_capability("test5"))); // expected-warning {{'release_capability' attribute only applies to functions}} -struct __attribute__((capability(12))) Test3 {}; // expected-error {{'capability' attribute requires a string}} -struct __attribute__((shared_capability(Test2))) Test4 {}; // expected-error {{'shared_capability' attribute requires a string}} +struct __attribute__((capability(12))) Test3 {}; // expected-error {{expected string literal as argument of 'capability' attribute}} +struct __attribute__((shared_capability(Test2))) Test4 {}; // expected-error {{expected string literal as argument of 'shared_capability' attribute}} struct __attribute__((capability)) Test5 {}; // expected-error {{'capability' attribute takes one argument}} struct __attribute__((shared_capability("test1", 12))) Test6 {}; // expected-error {{'shared_capability' attribute takes one argument}} diff --git a/clang/test/Sema/attr-enforce-tcb-errors.cpp b/clang/test/Sema/attr-enforce-tcb-errors.cpp --- a/clang/test/Sema/attr-enforce-tcb-errors.cpp +++ b/clang/test/Sema/attr-enforce-tcb-errors.cpp @@ -6,14 +6,14 @@ void too_many_arguments() __attribute__((enforce_tcb("test", 12))); // expected-error{{'enforce_tcb' attribute takes one argument}} -void wrong_argument_type() __attribute__((enforce_tcb(12))); // expected-error{{'enforce_tcb' attribute requires a string}} +void wrong_argument_type() __attribute__((enforce_tcb(12))); // expected-error{{expected string literal as argument of 'enforce_tcb' attribute}} [[clang::enforce_tcb_leaf("oops")]] int wrong_subject_type_leaf; // expected-warning{{'enforce_tcb_leaf' attribute only applies to functions}} void no_arguments_leaf() __attribute__((enforce_tcb_leaf)); // expected-error{{'enforce_tcb_leaf' attribute takes one argument}} void too_many_arguments_leaf() __attribute__((enforce_tcb_leaf("test", 12))); // expected-error{{'enforce_tcb_leaf' attribute takes one argument}} -void wrong_argument_type_leaf() __attribute__((enforce_tcb_leaf(12))); // expected-error{{'enforce_tcb_leaf' attribute requires a string}} +void wrong_argument_type_leaf() __attribute__((enforce_tcb_leaf(12))); // expected-error{{expected string literal as argument of 'enforce_tcb_leaf' attribute}} void foo(); diff --git a/clang/test/Sema/attr-enforce-tcb-errors.m b/clang/test/Sema/attr-enforce-tcb-errors.m --- a/clang/test/Sema/attr-enforce-tcb-errors.m +++ b/clang/test/Sema/attr-enforce-tcb-errors.m @@ -20,13 +20,13 @@ - (void)tooManyArguments __attribute__((enforce_tcb("test", 12))); // expected-error{{'enforce_tcb' attribute takes one argument}} -- (void)wrongArgumentType __attribute__((enforce_tcb(12))); // expected-error{{'enforce_tcb' attribute requires a string}} +- (void)wrongArgumentType __attribute__((enforce_tcb(12))); // expected-error{{expected string literal as argument of 'enforce_tcb' attribute}} - (void)noArgumentsLeaf __attribute__((enforce_tcb_leaf)); // expected-error{{'enforce_tcb_leaf' attribute takes one argument}} - (void)tooManyArgumentsLeaf __attribute__((enforce_tcb_leaf("test", 12))); // expected-error{{'enforce_tcb_leaf' attribute takes one argument}} -- (void)wrongArgumentTypeLeaf __attribute__((enforce_tcb_leaf(12))); // expected-error{{'enforce_tcb_leaf' attribute requires a string}} +- (void)wrongArgumentTypeLeaf __attribute__((enforce_tcb_leaf(12))); // expected-error{{expected string literal as argument of 'enforce_tcb_leaf' attribute}} @end @implementation AClass diff --git a/clang/test/Sema/attr-error.c b/clang/test/Sema/attr-error.c --- a/clang/test/Sema/attr-error.c +++ b/clang/test/Sema/attr-error.c @@ -15,7 +15,7 @@ __attribute__((error("bad2"))); // expected-error {{'error' attribute cannot be applied to a statement}} } -__attribute__((error(3))) // expected-error {{'error' attribute requires a string}} +__attribute__((error(3))) // expected-error {{expected string literal as argument of 'error' attribute}} int bad3(void); diff --git a/clang/test/Sema/attr-handles.cpp b/clang/test/Sema/attr-handles.cpp --- a/clang/test/Sema/attr-handles.cpp +++ b/clang/test/Sema/attr-handles.cpp @@ -6,7 +6,7 @@ auto lambda = [](int handle [[clang::use_handle("Fuchsia")]]){}; void g(int a __attribute__((acquire_handle("Fuchsia")))); // expected-error {{attribute only applies to output parameters}} void h(int *a __attribute__((acquire_handle))); // expected-error {{'acquire_handle' attribute takes one argument}} -void h(int *a __attribute__((acquire_handle(1)))); // expected-error {{attribute requires a string}} +void h(int *a __attribute__((acquire_handle(1)))); // expected-error {{expected string literal as argument of 'acquire_handle' attribute}} void h(int *a __attribute__((acquire_handle("RandomString", "AndAnother")))); // expected-error {{'acquire_handle' attribute takes one argument}} __attribute__((release_handle("Fuchsia"))) int i(); // expected-warning {{'release_handle' attribute only applies to parameters}} __attribute__((use_handle("Fuchsia"))) int j(); // expected-warning {{'use_handle' attribute only applies to parameters}} diff --git a/clang/test/Sema/attr-section.c b/clang/test/Sema/attr-section.c --- a/clang/test/Sema/attr-section.c +++ b/clang/test/Sema/attr-section.c @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -verify -fsyntax-only -triple x86_64-apple-darwin9 %s int x __attribute__((section( - 42))); // expected-error {{'section' attribute requires a string}} + 42))); // expected-error {{expected string literal as argument of 'section' attribute}} // rdar://4341926 diff --git a/clang/test/Sema/attr-tls_model.c b/clang/test/Sema/attr-tls_model.c --- a/clang/test/Sema/attr-tls_model.c +++ b/clang/test/Sema/attr-tls_model.c @@ -10,5 +10,5 @@ static __thread int y __attribute((tls_model("global-dynamic"))); // no-warning static __thread int y __attribute((tls_model("local", "dynamic"))); // expected-error {{'tls_model' attribute takes one argument}} -static __thread int y __attribute((tls_model(123))); // expected-error {{'tls_model' attribute requires a string}} +static __thread int y __attribute((tls_model(123))); // expected-error {{expected string literal as argument of 'tls_model' attribute}} static __thread int y __attribute((tls_model("foobar"))); // expected-error {{tls_model must be "global-dynamic", "local-dynamic", "initial-exec" or "local-exec"}} diff --git a/clang/test/Sema/attr-unavailable-message.c b/clang/test/Sema/attr-unavailable-message.c --- a/clang/test/Sema/attr-unavailable-message.c +++ b/clang/test/Sema/attr-unavailable-message.c @@ -8,7 +8,7 @@ void bar(void) __attribute__((__unavailable__)); // expected-note {{explicitly marked unavailable}} -int quux(void) __attribute__((__unavailable__(12))); // expected-error {{'__unavailable__' attribute requires a string}} +int quux(void) __attribute__((__unavailable__(12))); // expected-error {{expected string literal as argument of '__unavailable__' attribute}} #define ACCEPTABLE "Use something else" int quux2(void) __attribute__((__unavailable__(ACCEPTABLE))); diff --git a/clang/test/Sema/attr-warning.c b/clang/test/Sema/attr-warning.c --- a/clang/test/Sema/attr-warning.c +++ b/clang/test/Sema/attr-warning.c @@ -15,7 +15,7 @@ __attribute__((warning("bad2"))); // expected-error {{'warning' attribute cannot be applied to a statement}} } -__attribute__((warning(3))) // expected-error {{'warning' attribute requires a string}} +__attribute__((warning(3))) // expected-error {{expected string literal as argument of 'warning' attribute}} int bad3(void); diff --git a/clang/test/Sema/diagnose_if.c b/clang/test/Sema/diagnose_if.c --- a/clang/test/Sema/diagnose_if.c +++ b/clang/test/Sema/diagnose_if.c @@ -6,7 +6,7 @@ void failure2(void) _diagnose_if(0); // expected-error{{exactly 3 arguments}} void failure3(void) _diagnose_if(0, ""); // expected-error{{exactly 3 arguments}} void failure4(void) _diagnose_if(0, "", "error", 1); // expected-error{{exactly 3 arguments}} -void failure5(void) _diagnose_if(0, 0, "error"); // expected-error{{requires a string}} +void failure5(void) _diagnose_if(0, 0, "error"); // expected-error{{expected string literal as argument of 'diagnose_if' attribute}} void failure6(void) _diagnose_if(0, "", "invalid"); // expected-error{{invalid diagnostic type for 'diagnose_if'; use "error" or "warning" instead}} void failure7(void) _diagnose_if(0, "", "ERROR"); // expected-error{{invalid diagnostic type}} void failure8(int a) _diagnose_if(a, "", ""); // expected-error{{invalid diagnostic type}} diff --git a/clang/test/Sema/enable_if.c b/clang/test/Sema/enable_if.c --- a/clang/test/Sema/enable_if.c +++ b/clang/test/Sema/enable_if.c @@ -105,7 +105,7 @@ int n __attribute__((enable_if(1, "always chosen"))); // expected-warning{{'enable_if' attribute only applies to functions}} -void f(int n) __attribute__((enable_if("chosen when 'n' is zero", n == 0))); // expected-error{{'enable_if' attribute requires a string}} +void f(int n) __attribute__((enable_if("chosen when 'n' is zero", n == 0))); // expected-error{{expected string literal as argument of 'enable_if' attribute}} void f(int n) __attribute__((enable_if())); // expected-error{{'enable_if' attribute requires exactly 2 arguments}} diff --git a/clang/test/SemaCXX/attr-deprecated-replacement-error.cpp b/clang/test/SemaCXX/attr-deprecated-replacement-error.cpp --- a/clang/test/SemaCXX/attr-deprecated-replacement-error.cpp +++ b/clang/test/SemaCXX/attr-deprecated-replacement-error.cpp @@ -5,13 +5,13 @@ #endif int a1 [[deprecated("warning", "fixit")]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} -int a2 [[deprecated("warning", 1)]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} +int a2 [[deprecated("warning", 1)]]; // expected-error{{expected string literal as argument of 'deprecated' attribute}} int b1 [[gnu::deprecated("warning", "fixit")]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} -int b2 [[gnu::deprecated("warning", 1)]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} +int b2 [[gnu::deprecated("warning", 1)]]; // expected-error{{expected string literal as argument of 'deprecated' attribute}} __declspec(deprecated("warning", "fixit")) int c1; // expected-error{{'deprecated' attribute takes no more than 1 argument}} -__declspec(deprecated("warning", 1)) int c2; // expected-error{{'deprecated' attribute takes no more than 1 argument}} +__declspec(deprecated("warning", 1)) int c2; // expected-error{{expected string literal as argument of 'deprecated' attribute}} int d1 __attribute__((deprecated("warning", "fixit"))); -int d2 __attribute__((deprecated("warning", 1))); // expected-error{{'deprecated' attribute requires a string}} +int d2 __attribute__((deprecated("warning", 1))); // expected-error{{expected string literal as argument of 'deprecated' attribute}} diff --git a/clang/test/SemaCXX/attr-no-sanitize.cpp b/clang/test/SemaCXX/attr-no-sanitize.cpp --- a/clang/test/SemaCXX/attr-no-sanitize.cpp +++ b/clang/test/SemaCXX/attr-no-sanitize.cpp @@ -4,7 +4,7 @@ int f1() __attribute__((no_sanitize)); // expected-error{{'no_sanitize' attribute takes at least 1 argument}} -int f2() __attribute__((no_sanitize(1))); // expected-error{{'no_sanitize' attribute requires a string}} +int f2() __attribute__((no_sanitize(1))); // expected-error{{expected string literal as argument of 'no_sanitize' attribute}} __attribute__((no_sanitize("all"))) int global; // expected-warning{{'no_sanitize' attribute argument 'all' not supported on a global variable}} __attribute__((no_sanitize("unknown"))) int global2; // expected-warning{{unknown sanitizer 'unknown' ignored}} diff --git a/clang/test/SemaCXX/attr-section.cpp b/clang/test/SemaCXX/attr-section.cpp --- a/clang/test/SemaCXX/attr-section.cpp +++ b/clang/test/SemaCXX/attr-section.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -verify -fsyntax-only -triple x86_64-linux-gnu %s int x __attribute__((section( - 42))); // expected-error {{'section' attribute requires a string}} + 42))); // expected-error {{expected string literal as argument of 'section' attribute}} // PR6007 diff --git a/clang/test/SemaCXX/attr-weakref.cpp b/clang/test/SemaCXX/attr-weakref.cpp --- a/clang/test/SemaCXX/attr-weakref.cpp +++ b/clang/test/SemaCXX/attr-weakref.cpp @@ -33,6 +33,6 @@ static int a10(); int a10() __attribute__((weakref ("foo"))); -static int v __attribute__((weakref(a1), alias("foo"))); // expected-error {{'weakref' attribute requires a string}} +static int v __attribute__((weakref(a1), alias("foo"))); // expected-error {{expected string literal as argument of 'weakref' attribute}} __attribute__((weakref ("foo"))) auto a11 = 1; // expected-error {{weakref declaration must have internal linkage}} diff --git a/clang/test/SemaCXX/suppress.cpp b/clang/test/SemaCXX/suppress.cpp --- a/clang/test/SemaCXX/suppress.cpp +++ b/clang/test/SemaCXX/suppress.cpp @@ -16,7 +16,7 @@ [[gsl::suppress]] int x; // expected-error {{'suppress' attribute takes at least 1 argument}} [[gsl::suppress()]] int y; // expected-error {{'suppress' attribute takes at least 1 argument}} int [[gsl::suppress("r")]] z; // expected-error {{'suppress' attribute cannot be applied to types}} - [[gsl::suppress(f_)]] float f; // expected-error {{'suppress' attribute requires a string}} + [[gsl::suppress(f_)]] float f; // expected-error {{expected string literal as argument of 'suppress' attribut}} } union [[gsl::suppress("type.1")]] U { diff --git a/clang/test/SemaObjC/attr-swift_bridge.m b/clang/test/SemaObjC/attr-swift_bridge.m --- a/clang/test/SemaObjC/attr-swift_bridge.m +++ b/clang/test/SemaObjC/attr-swift_bridge.m @@ -5,7 +5,7 @@ @interface I @end -// expected-error@+1 {{'__swift_bridge__' attribute requires a string}} +// expected-error@+1 {{expected string literal as argument of '__swift_bridge__' attribute}} __attribute__((__swift_bridge__(1))) @interface J @end diff --git a/clang/test/SemaObjC/objc-asm-attribute-neg-test.m b/clang/test/SemaObjC/objc-asm-attribute-neg-test.m --- a/clang/test/SemaObjC/objc-asm-attribute-neg-test.m +++ b/clang/test/SemaObjC/objc-asm-attribute-neg-test.m @@ -5,7 +5,7 @@ @interface BInterface @end -__attribute__((objc_runtime_name(123))) // expected-error {{'objc_runtime_name' attribute requires a string}} +__attribute__((objc_runtime_name(123))) // expected-error {{expected string literal as argument of 'objc_runtime_name' attribute}} @protocol BProtocol1 @end @@ -14,7 +14,7 @@ @end __attribute__((objc_runtime_name("MySecretNamespace.Message"))) -@interface Message { +@interface Message { __attribute__((objc_runtime_name("MySecretNamespace.Message"))) // expected-error {{'objc_runtime_name' attribute only applies to Objective-C interfaces and Objective-C protocols}} id MyIVAR; } diff --git a/clang/test/SemaObjC/validate-attr-swift_attr.m b/clang/test/SemaObjC/validate-attr-swift_attr.m --- a/clang/test/SemaObjC/validate-attr-swift_attr.m +++ b/clang/test/SemaObjC/validate-attr-swift_attr.m @@ -5,7 +5,7 @@ @interface I @end -// expected-error@+1 {{'swift_attr' attribute requires a string}} +// expected-error@+1 {{expected string literal as argument of 'swift_attr' attribute}} __attribute__((swift_attr(1))) @interface J @end diff --git a/clang/test/SemaTemplate/attributes.cpp b/clang/test/SemaTemplate/attributes.cpp --- a/clang/test/SemaTemplate/attributes.cpp +++ b/clang/test/SemaTemplate/attributes.cpp @@ -25,7 +25,7 @@ { __attribute__((aligned(Align))) char storage[Size]; }; - + template class C { public: @@ -95,11 +95,11 @@ template [[clang::annotate("ANNOTATE_BAZ", Is...)]] void HasPackAnnotations(); void UsePackAnnotations() { HasPackAnnotations<1, 2, 3>(); } -template [[clang::annotate(Is...)]] void HasOnlyPackAnnotation() {} // expected-error {{'annotate' attribute takes at least 1 argument}} expected-error {{'annotate' attribute requires a string}} +template [[clang::annotate(Is...)]] void HasOnlyPackAnnotation() {} // expected-error {{expected string literal as argument of 'annotate' attribute}} void UseOnlyPackAnnotations() { - HasOnlyPackAnnotation<>(); // expected-note {{in instantiation of function template specialization 'attribute_annotate::HasOnlyPackAnnotation<>' requested here}} - HasOnlyPackAnnotation<1>(); // expected-note {{in instantiation of function template specialization 'attribute_annotate::HasOnlyPackAnnotation<1>' requested here}} + HasOnlyPackAnnotation<>(); + HasOnlyPackAnnotation<1>(); } // CHECK: ClassTemplateDecl {{.*}} AnnotatedPackTemplateStruct @@ -276,40 +276,21 @@ // CHECK-NEXT: value: Int 6 // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 6 // CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct -// CHECK-NEXT: ClassTemplatePartialSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition -// CHECK-NEXT: DefinitionData -// CHECK-NEXT: DefaultConstructor -// CHECK-NEXT: CopyConstructor -// CHECK-NEXT: MoveConstructor -// CHECK-NEXT: CopyAssignment -// CHECK-NEXT: MoveAssignment -// CHECK-NEXT: Destructor -// CHECK-NEXT: TemplateArgument{{.*}} type 'char' -// CHECK-NEXT: BuiltinType {{.*}} 'char' -// CHECK-NEXT: TemplateArgument{{.*}} pack -// CHECK-NEXT: TemplateArgument{{.*}} expr -// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' -// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' -// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is -// CHECK-NEXT: AnnotateAttr {{.*}} "" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' -// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' -// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct template struct [[clang::annotate("ANNOTATE_FOZ", Is...)]] AnnotatedPackTemplateStruct{}; template struct [[clang::annotate("ANNOTATE_BOO", Is...)]] AnnotatedPackTemplateStruct{}; template struct [[clang::annotate("ANNOTATE_FOZ", 4, 5, 6)]] AnnotatedPackTemplateStruct{}; -template struct [[clang::annotate(Is...)]] AnnotatedPackTemplateStruct{}; // expected-error {{'annotate' attribute requires a string}} expected-error {{'annotate' attribute takes at least 1 argument}} +template struct [[clang::annotate(Is...)]] AnnotatedPackTemplateStruct{}; // expected-error {{expected string literal as argument of 'annotate' attribute}} void UseAnnotatedPackTemplateStructSpecializations() { AnnotatedPackTemplateStruct Instance1{}; AnnotatedPackTemplateStruct Instance2{}; AnnotatedPackTemplateStruct Instance3{}; - AnnotatedPackTemplateStruct Instance4{}; // expected-note {{in instantiation of template class 'attribute_annotate::AnnotatedPackTemplateStruct' requested here}} - AnnotatedPackTemplateStruct Instance5{}; // expected-note {{in instantiation of template class 'attribute_annotate::AnnotatedPackTemplateStruct' requested here}} + AnnotatedPackTemplateStruct Instance4{}; + AnnotatedPackTemplateStruct Instance5{}; } // CHECK: ClassTemplateDecl {{.*}} InvalidAnnotatedPackTemplateStruct // CHECK-NEXT: TemplateTypeParmDecl {{.*}} typename depth 0 index 0 T -// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 1 ... Is +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} 'int' depth 0 index 1 ... Is // CHECK-NEXT: CXXRecordDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition // CHECK-NEXT: DefinitionData // CHECK-NEXT: DefaultConstructor @@ -318,9 +299,6 @@ // CHECK-NEXT: CopyAssignment // CHECK-NEXT: MoveAssignment // CHECK-NEXT: Destructor -// CHECK-NEXT: AnnotateAttr {{.*}} "" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' -// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct InvalidAnnotatedPackTemplateStruct // CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'InvalidAnnotatedPackTemplateStruct' // CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition @@ -446,7 +424,7 @@ // CHECK-NEXT: TemplateArgument{{.*}} integral 6 // CHECK-NEXT: TemplateArgument{{.*}} integral 7 // CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct InvalidAnnotatedPackTemplateStruct -template struct [[clang::annotate(Is...)]] InvalidAnnotatedPackTemplateStruct{}; // expected-error {{'annotate' attribute requires a string}} expected-error {{'annotate' attribute takes at least 1 argument}} +template struct InvalidAnnotatedPackTemplateStruct{}; template struct [[clang::annotate("ANNOTATE_BIR", Is...)]] InvalidAnnotatedPackTemplateStruct{}; template struct InvalidAnnotatedPackTemplateStruct {}; template <> struct InvalidAnnotatedPackTemplateStruct {}; @@ -454,8 +432,8 @@ InvalidAnnotatedPackTemplateStruct Instance1{}; InvalidAnnotatedPackTemplateStruct Instance2{}; InvalidAnnotatedPackTemplateStruct Instance3{}; - InvalidAnnotatedPackTemplateStruct Instance4{}; // expected-note {{in instantiation of template class 'attribute_annotate::InvalidAnnotatedPackTemplateStruct' requested here}} - InvalidAnnotatedPackTemplateStruct Instance5{}; // expected-note {{in instantiation of template class 'attribute_annotate::InvalidAnnotatedPackTemplateStruct' requested here}} + InvalidAnnotatedPackTemplateStruct Instance4{}; + InvalidAnnotatedPackTemplateStruct Instance5{}; } // CHECK: FunctionTemplateDecl {{.*}} RedeclaredAnnotatedFunc diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -2297,6 +2297,22 @@ .Default(false); } +static bool isStringLiteralArgument(const Record *Arg) { + return !Arg->getSuperClasses().empty() && + llvm::StringSwitch( + Arg->getSuperClasses().back().first->getName()) + .Case("StringArgument", true) + .Default(false); +} + +static bool isVariadicStringLiteralArgument(const Record *Arg) { + return !Arg->getSuperClasses().empty() && + llvm::StringSwitch( + Arg->getSuperClasses().back().first->getName()) + .Case("VariadicStringArgument", true) + .Default(false); +} + static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records, raw_ostream &OS) { OS << "#if defined(CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST)\n"; @@ -2317,6 +2333,34 @@ OS << "#endif // CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST\n\n"; } +// Emits the list of arguments that shoulkd be parsed as unevaluated string +// literals for each attributes +static void emitClangAttrUnevaluatedStringLiteralList(RecordKeeper &Records, + raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_STRING_LITERAL_ARG_LIST)\n"; + std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); + for (const auto *Attr : Attrs) { + std::vector Args = Attr->getValueAsListOfDefs("Args"); + uint32_t Bits = 0; + for (uint32_t N = 0; N < Args.size(); N++) { + Bits |= (isStringLiteralArgument(Args[N]) << N); + // If we have a variadic string argument, set all the remaining bits to 1 + if (isVariadicStringLiteralArgument(Args[N])) { + for (; N < sizeof(uint32_t); N++) + Bits |= (1 << N); + break; + } + } + if (!Bits) + continue; + // All these spellings have at least one string literal has argument. + forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.name() << "\", " << Bits << ")\n"; + }); + } + OS << "#endif // CLANG_ATTR_STRING_LITERAL_ARG_LIST\n\n"; +} + // Emits the first-argument-is-identifier property for attributes. static void emitClangAttrIdentifierArgList(RecordKeeper &Records, raw_ostream &OS) { OS << "#if defined(CLANG_ATTR_IDENTIFIER_ARG_LIST)\n"; @@ -4611,6 +4655,7 @@ emitSourceFileHeader("Parser-related llvm::StringSwitch cases", OS); emitClangAttrArgContextList(Records, OS); emitClangAttrIdentifierArgList(Records, OS); + emitClangAttrUnevaluatedStringLiteralList(Records, OS); emitClangAttrVariadicIdentifierArgList(Records, OS); emitClangAttrThisIsaIdentifierArgList(Records, OS); emitClangAttrAcceptsExprPack(Records, OS); diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -115,12 +115,7 @@ Unevaluated strings P2361R6 - -
- Clang 17 (Partial) - Attributes arguments don't yet parse as unevaluated string literals. -
- + Clang 18 Add @, $, and ` to the basic character set