diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 57a94242c9e61..908a719015da1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -684,6 +684,11 @@ Improvements to Clang's diagnostics - Clang now tries to avoid printing file paths that contain ``..``, instead preferring the canonical file path if it ends up being shorter. +- Clang rejects the ``#`` and ``##`` preprocessor tokens in an attribute + argument list in C++. The operators can be used in macro replacement lists + with the usual preprocessor semantics. What is rejected are non-preprocessor + uses of the tokens. The same restrictions do not apply in C. (#GH147217) + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index fd8e5c3c6ad87..424b7034c6114 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -830,6 +830,8 @@ def err_ms_property_expected_comma_or_rparen : Error< "expected ',' or ')' at end of property accessor list">; def err_ms_property_initializer : Error< "property declaration cannot have a default member initializer">; +def err_invalid_attribute_argument + : Error<"'%0' is not allowed in an attribute argument list">; def err_assume_attr_expects_cond_expr : Error< "use of this expression in an %0 attribute requires parentheses">; diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index eae8281964692..66c1b9a11e4e0 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -4520,6 +4520,27 @@ bool Parser::ParseCXX11AttributeArgs( Form = ParsedAttr::Form::Microsoft(); } + if (LO.CPlusPlus) { + TentativeParsingAction TPA(*this); + bool HasInvalidArgument = false; + while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::eof)) { + if (Tok.isOneOf(tok::hash, tok::hashhash)) { + Diag(Tok.getLocation(), diag::err_invalid_attribute_argument) + << PP.getSpelling(Tok); + HasInvalidArgument = true; + } + ConsumeAnyToken(); + } + + if (HasInvalidArgument) { + SkipUntil(tok::r_paren); + TPA.Commit(); + return true; + } + + TPA.Revert(); + } + // If the attribute isn't known, we will not attempt to parse any // arguments. if (Form.getSyntax() != ParsedAttr::AS_Microsoft && diff --git a/clang/test/Parser/cxx0x-attributes-preprocessor-tokens.cpp b/clang/test/Parser/cxx0x-attributes-preprocessor-tokens.cpp new file mode 100644 index 0000000000000..23a9d597e5dd8 --- /dev/null +++ b/clang/test/Parser/cxx0x-attributes-preprocessor-tokens.cpp @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -E %s | FileCheck %s +// RUN: %clang_cc1 -x c -fsyntax-only -verify=c %s +// RUN: %clang_cc1 -x c -E %s | FileCheck %s + +#define ATTR_STR(X) [[clang::annotate(#X)]] +#define ATTR_PASTE(X, Y) [[clang::annotate("test", X ## Y)]] + +[[clang::assume(#)]] void f1(); // c-error {{expected expression}} \ + // expected-error {{'#' is not allowed in an attribute argument list}} + +[[clang::assume(##)]] void f2(); // c-error {{expected expression}} \ + // expected-error {{'##' is not allowed in an attribute argument list}} + +[[clang::assume(1#2#3)]] void f3(); // c-error {{use of this expression in an 'assume' attribute requires parentheses}} \ + // c-error {{expected ')'}} \ + // c-note {{to match this '('}} \ + // expected-error {{'#' is not allowed in an attribute argument list}} \ + // expected-error {{'#' is not allowed in an attribute argument list}} + +[[unknown::unknown(#)]] void f4(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-error {{'#' is not allowed in an attribute argument list}} + +[[unknown::unknown(##)]] void f5(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-error {{'##' is not allowed in an attribute argument list}} + +[[unknown::unknown(1#2#3)]] void f6(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-error {{'#' is not allowed in an attribute argument list}} \ + // expected-error {{'#' is not allowed in an attribute argument list}} + +[[clang::assume(%:)]] void f7(); // c-error {{expected expression}} \ + // expected-error {{'%:' is not allowed in an attribute argument list}} + + +[[clang::assume(%:%:)]] void f8(); // c-error {{expected expression}} \ + // expected-error {{'%:%:' is not allowed in an attribute argument list}} + +[[clang::assume(1%:2%:3)]] void f9(); // c-error {{use of this expression in an 'assume' attribute requires parentheses}} \ + // c-error {{expected ')'}} \ + // c-note {{to match this '('}} \ + // expected-error {{'%:' is not allowed in an attribute argument list}} \ + // expected-error {{'%:' is not allowed in an attribute argument list}} + +[[unknown::unknown(%:)]] void f10(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-error {{'%:' is not allowed in an attribute argument list}} + +[[unknown::unknown(%:%:)]] void f11(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-error {{'%:%:' is not allowed in an attribute argument list}} + +[[unknown::unknown(1%:2%:3)]] void f12(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-error {{'%:' is not allowed in an attribute argument list}} \ + // expected-error {{'%:' is not allowed in an attribute argument list}} + +ATTR_STR(stringify) void f13(); +// CHECK: {{\[\[}}clang{{::}}annotate("stringify"){{\]\]}} void f13(); + +ATTR_PASTE(1, 2) void f14(); +// CHECK: {{\[\[}}clang{{::}}annotate("test", 12){{\]\]}} void f14();