diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0d85b6f426995..465c50784f27f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -228,6 +228,12 @@ Improvements to Clang's diagnostics - Fixed false positive in ``-Wmissing-noreturn`` diagnostic when it was requiring the usage of ``[[noreturn]]`` on lambdas before C++23 (#GH154493). +- Clang now diagnoses the use of ``#`` and ``##`` preprocessor tokens in + attribute argument lists in C++ when ``-pedantic`` is enabled. The operators + can be used in macro replacement lists with the usual preprocessor semantics, + however, non-preprocessor use of tokens now triggers a pedantic warning in C++. + Compilation in C mode is unchanged, and still permits these tokens to be used. (#GH147217) + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 0042afccba2c8..3a6a9e582c7ca 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -830,6 +830,9 @@ 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 ext_invalid_attribute_argument + : Extension<"'%0' is not allowed in an attribute argument list">, + InGroup>; 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 3214e6f5fad2d..005ad524605ff 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -4512,6 +4512,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::ext_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..6605d2486aa03 --- /dev/null +++ b/clang/test/Parser/cxx0x-attributes-preprocessor-tokens.cpp @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -fsyntax-only -Wattribute-preprocessor-tokens -verify %s +// RUN: %clang_cc1 -Wattribute-preprocessor-tokens -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-warning {{'#' is not allowed in an attribute argument list}} + +[[clang::assume(##)]] void f2(); // c-error {{expected expression}} \ + // expected-warning {{'##' 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-warning {{'#' is not allowed in an attribute argument list}} \ + // expected-warning {{'#' is not allowed in an attribute argument list}} + +[[unknown::unknown(#)]] void f4(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-warning {{'#' is not allowed in an attribute argument list}} + +[[unknown::unknown(##)]] void f5(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-warning {{'##' is not allowed in an attribute argument list}} + +[[unknown::unknown(1#2#3)]] void f6(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-warning {{'#' is not allowed in an attribute argument list}} \ + // expected-warning {{'#' is not allowed in an attribute argument list}} + +[[clang::assume(%:)]] void f7(); // c-error {{expected expression}} \ + // expected-warning {{'%:' is not allowed in an attribute argument list}} + + +[[clang::assume(%:%:)]] void f8(); // c-error {{expected expression}} \ + // expected-warning {{'%:%:' 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-warning {{'%:' is not allowed in an attribute argument list}} \ + // expected-warning {{'%:' is not allowed in an attribute argument list}} + +[[unknown::unknown(%:)]] void f10(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-warning {{'%:' is not allowed in an attribute argument list}} + +[[unknown::unknown(%:%:)]] void f11(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-warning {{'%:%:' is not allowed in an attribute argument list}} + +[[unknown::unknown(1%:2%:3)]] void f12(); // c-warning {{unknown attribute 'unknown::unknown' ignored}} \ + // expected-warning {{'%:' is not allowed in an attribute argument list}} \ + // expected-warning {{'%:' 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();